Skip to content
RED SAGSkills

iOS interview questions and answers

iOS interview questions cover Swift, app architecture and how apps behave on Apple devices.

Below are 60 iOS questions with the correct answer and a short explanation, grouped from fresher to experienced level. Try each one before you open the answer.

What iOS interviews usually check

  • ✓SwiftUI and UIKit
  • ✓App lifecycle
  • ✓Memory management and retain cycles
  • ✓Concurrency with async/await
  • ✓Networking and persistence

iOS interview questions for freshers easy

1. Safely unwrapping a Swift optional

name is of type String?. Which is the safest way to use it only when it has a value?

  1. A.if let name { print(name) }
  2. B.print(name!)
  3. C.print(name as String)
  4. D.print(String(name))
Show answer

Answer: A. if let name { print(name) }

Optional binding with if let (and the short form if let name in Swift 5.7+) runs the block only when the value is not nil and gives you a non-optional value. Force unwrapping with ! crashes when the value is nil, and the other two do not compile for an optional.

2. Swift class shared reference

What does this Swift code print?

class Counter { var value = 0 }
let a = Counter()
let b = a
b.value = 3
print(a.value)
  1. A.0
  2. B.Compile error because b is declared with let
  3. C.nil
  4. D.3
Show answer

Answer: D. 3

Classes are reference types, so a and b point to the same Counter object. 'let' only stops you from pointing b at a different object; it does not freeze the object's var properties. Setting b.value to 3 is therefore visible through a.

3. Decoding JSON with extra keys

What does this Swift code print?

struct User: Codable {
  let id: Int
  let name: String
}
let json = #"{"id": 7, "name": "Sara", "age": 30}"#.data(using: .utf8)!
let user = try JSONDecoder().decode(User.self, from: json)
print(user.name)
  1. A.A DecodingError, because age is not a property of User
  2. B.Sara
  3. C.nil
  4. D.Optional("Sara")
Show answer

Answer: B. Sara

JSONDecoder ignores keys in the JSON that the type does not declare, so the extra age field is fine. name is a non-optional String, so print shows Sara without the Optional wrapper. Decoding fails only when a required key is missing or has the wrong type.

4. What await means in Swift

What happens at an await when calling an async function in Swift?

  1. A.The current thread is blocked until the result arrives, but the system starts a second main thread so the UI keeps responding
  2. B.The function may suspend there, freeing the thread for other work, and resumes when the result is ready
  3. C.The call runs in a separate process
  4. D.The call is skipped when the device is offline
Show answer

Answer: B. The function may suspend there, freeing the thread for other work, and resumes when the result is ready

await marks a possible suspension point. While the async work is in progress, the thread is free to run other code, such as drawing the UI, and the function continues later. This is why awaiting on the main actor does not freeze the app, unlike a blocking call.

5. iOS build fails after adding a library

After `npm install` of a library with native iOS code, the iOS build fails with a missing module error. What step was likely skipped?

  1. A.Restarting the Metro bundler twice
  2. B.Running pod install in the ios folder
  3. C.Clearing the Android Gradle cache
  4. D.Running npm audit fix --force
Show answer

Answer: B. Running pod install in the ios folder

Native iOS dependencies are managed by CocoaPods. After adding or updating a library with native code, pod install (or npx pod-install) links its pods into the Xcode workspace. Expo projects do this as part of prebuild or EAS Build.

6. iOS app moved to the background

A user presses the home gesture while your iOS app is open and does not return. With no background mode active, what normally happens soon after?

  1. A.The app keeps running all code until the battery is low
  2. B.The app is suspended in memory and may later be terminated
  3. C.The app is deleted from the phone by the system
  4. D.The app restarts automatically every few minutes
Show answer

Answer: B. The app is suspended in memory and may later be terminated

iOS moves an app through inactive to background, gives it a short time to finish work, and then suspends it so it uses no CPU. A suspended app can be terminated without notice to free memory. Long work needs an approved background mode or background tasks.

7. viewDidLoad or viewWillAppear

A UIKit screen should refresh its unread count every time it comes back on screen, not only the first time. Where does the refresh belong?

  1. A.viewDidLoad
  2. B.init(coder:)
  3. C.deinit
  4. D.viewWillAppear
Show answer

Answer: D. viewWillAppear

viewDidLoad runs once, after the view hierarchy is loaded into memory, so it suits one-time setup. viewWillAppear runs each time the view is about to become visible, including when popping back to it. deinit runs when the controller is freed.

8. Urgent fix waiting in review

A critical bug fix for a payments crash is waiting in App Store review. What can the team request?

  1. A.An expedited review, explaining the critical issue
  2. B.A refund of the developer fee to skip review
  3. C.Direct installation on users' phones by Apple
  4. D.Removal of the review step for all future builds
Show answer

Answer: A. An expedited review, explaining the critical issue

Apple accepts expedited review requests for critical bug fixes and time-sensitive events. It is not guaranteed and should be used sparingly. Teams also reduce the need for it with feature flags and server-side fixes.

9. What guard let is for

Why is guard let common at the start of functions?

  1. A.It unwraps values and exits early if they are nil
  2. B.It runs the rest of the function on a background thread
  3. C.It repeats the function until a value is not nil
  4. D.It turns every optional into a default value
Show answer

Answer: A. It unwraps values and exits early if they are nil

guard let checks a condition and requires the else branch to leave the scope with return, throw, break or continue. After the guard, the unwrapped value is available for the rest of the function, avoiding deep nesting.

10. Giving an optional a default

What is the value of name when user.nickname is nil?

let name = user.nickname ?? "Guest"
  1. A.nil
  2. B.An empty string
  3. C."Guest"
  4. D.It crashes at runtime
Show answer

Answer: C. "Guest"

The nil-coalescing operator ?? returns the unwrapped left side if it has a value, otherwise the right side. The result here is a non-optional String.

11. Force unwrapping nil

What happens when this runs and the text is not a number?

let value = Int(textField.text ?? "")!
  1. A.The app crashes with a fatal error
  2. B.value is quietly set to 0
  3. C.value is quietly set to nil
  4. D.The whole line is skipped
Show answer

Answer: A. The app crashes with a fatal error

Int("abc") returns nil, and the ! force unwrap traps at runtime when the value is nil. Use if let, guard let or ?? with a default when input can be invalid.

12. A switch without default

An enum OrderStatus has cases placed, shipped and delivered. A switch handles only placed and shipped, with no default. What happens?

  1. A.delivered silently does nothing at all
  2. B.It crashes at runtime when delivered is passed
  3. C.delivered falls through into the shipped case
  4. D.A compile error: the switch must be exhaustive
Show answer

Answer: D. A compile error: the switch must be exhaustive

Swift switch statements must cover every possible value. For enums, the compiler checks each case. Avoiding default for your own enums means adding a case later produces errors where it must be handled.

13. What try? returns

What is the type of data here?

let data = try? Data(contentsOf: fileURL)
  1. A.Data
  2. B.Result<Data, Error>
  3. C.Data?
  4. D.Error?
Show answer

Answer: C. Data?

try? turns a throwing call into an optional: the value on success, nil if an error is thrown. The error itself is discarded, so use do/catch when you need to know what failed.

14. Reading a missing key

prices is [String: Int]. What does prices["mango"] return when the key is missing?

  1. A.0, the default value of Int
  2. B.It crashes with an index error
  3. C.An empty dictionary
  4. D.nil, because the subscript returns Int?
Show answer

Answer: D. nil, because the subscript returns Int?

Dictionary lookups return an optional because the key may not exist. You can supply a default with prices["mango", default: 0] or use ??.

15. A computed property

What is true about fullName?

struct Person {
    var first: String
    var last: String
    var fullName: String { "\(first) \(last)" }
}
  1. A.It stores the combined name when the struct is created
  2. B.It is recomputed from the other properties each time it is read
  3. C.It must be declared with let
  4. D.It can only be read once
Show answer

Answer: B. It is recomputed from the other properties each time it is read

A computed property has no storage; its getter runs on each access. This keeps derived values in sync with the stored properties they depend on.

16. Marking UI code with @MainActor

What does annotating a view model class with @MainActor guarantee?

  1. A.It runs all its methods on a background thread
  2. B.It makes the class a singleton
  3. C.Its properties and methods are used on the main thread
  4. D.It turns off strict concurrency checks
Show answer

Answer: C. Its properties and methods are used on the main thread

@MainActor isolates the type to the main actor, so the compiler ensures access happens on the main thread, and calls from other contexts need await. This is the natural home for UI state.

17. Mapping with a key path

What does users.map(\.name) return?

  1. A.An array of each user’s name
  2. B.A dictionary keyed by name
  3. C.The first user’s name only
  4. D.A sorted copy of users
Show answer

Answer: A. An array of each user’s name

Key path expressions like \.name can be used where a function from the root type to the value is expected. users.map(\.name) is the same as users.map { $0.name }.

18. Using a SwiftUI view in UIKit

An older UIKit app wants to show a new SwiftUI settings screen. Which type wraps the SwiftUI view as a view controller?

  1. A.UIViewRepresentable
  2. B.UINavigationBar
  3. C.UIStoryboardSegue
  4. D.UIHostingController
Show answer

Answer: D. UIHostingController

UIHostingController(rootView:) hosts a SwiftUI view inside UIKit and can be pushed, presented or added as a child. UIViewRepresentable goes the other way, putting a UIKit view inside SwiftUI.

19. Localising strings in Xcode

Since Xcode 15, what is the recommended file for managing app string translations?

  1. A.A plain .txt file per language
  2. B.A String Catalog (.xcstrings)
  3. C.A JSON file loaded at launch
  4. D.Hard-coded strings in each view
Show answer

Answer: B. A String Catalog (.xcstrings)

String Catalogs gather localisable strings from code automatically and show translation state per language in one editor. SwiftUI Text("Welcome") is localisable by default with a matching key.

20. Previewing a SwiftUI view

What does the #Preview macro do in a SwiftUI file?

  1. A.Runs the view on a real device on build
  2. B.Generates unit tests for the view
  3. C.Declares a live preview shown in Xcode’s canvas
  4. D.Ships the view to TestFlight
Show answer

Answer: C. Declares a live preview shown in Xcode’s canvas

#Preview { OrderRow(order: .sample) } registers a preview that Xcode renders in the canvas. It replaces the older PreviewProvider struct and works for SwiftUI views and UIKit views or view controllers.

iOS intermediate interview questions medium

21. SwiftUI .task when leaving a screen

A SwiftUI view loads data with .task { await vm.load() }. The user navigates away before loading finishes. What happens?

  1. A.The task keeps running until the app is terminated, because Swift tasks are never cancelled unless you call cancel() yourself
  2. B.SwiftUI cancels the task automatically when the view disappears, so load() should respect cancellation
  3. C.The app crashes because the view no longer exists
  4. D.The task moves to the next visible view
Show answer

Answer: B. SwiftUI cancels the task automatically when the view disappears, so load() should respect cancellation

The .task modifier ties an async task to the view lifetime and cancels it when the view goes away. Cancellation is cooperative: URLSession and Task.sleep throw CancellationError, but your own loops should check Task.isCancelled. This avoids wasted work and updates to screens nobody sees.

22. Unreadable iOS crash stack traces

Crashlytics shows iOS crash stack traces for the latest release as raw memory addresses with no function names. What is most likely missing?

  1. A.More logging statements in the app
  2. B.The crashes come from jailbroken devices, which strip function names from stack traces before sending them to Crashlytics
  3. C.Swift support in Crashlytics
  4. D.The dSYM files for that build were not uploaded, so the crashes cannot be symbolicated
Show answer

Answer: D. The dSYM files for that build were not uploaded, so the crashes cannot be symbolicated

Release binaries do not contain readable symbol names; the dSYM file maps addresses back to functions and line numbers. Each build has its own dSYM, so CI must upload it for every release. On Android, the R8 mapping file plays the same role for obfuscated stack traces.

23. The @Observable macro in SwiftUI

With Swift's @Observable macro (iOS 17+), what decides which views update when a property changes?

  1. A.Every view in the app on each property change
  2. B.Only views marked with @Published wrappers
  3. C.Only views that read that property in their body
  4. D.Views refresh on a fixed 60-times-a-second timer
Show answer

Answer: C. Only views that read that property in their body

@Observable tracks property reads during body evaluation, so SwiftUI updates only views that actually used the changed property. With ObservableObject, any @Published change notified every observing view. This finer tracking often reduces unnecessary updates.

24. Podcast stops when the screen locks

An iOS podcast app stops playing when the user locks the phone. What is missing?

  1. A.A Timer that restarts playback every second
  2. B.The audio background mode and a playback audio session
  3. C.The location permission set to Always
  4. D.A silent push notification sent every minute
Show answer

Answer: B. The audio background mode and a playback audio session

iOS suspends apps in the background unless they use an allowed background mode. Declaring the audio mode in UIBackgroundModes and setting the AVAudioSession category to playback keeps audio going. Apple reviews that the mode is used for real audio playback.

25. APNs key or certificate

Why do teams prefer an APNs authentication key (.p8) over per-app push certificates?

  1. A.A .p8 key lets pushes skip Apple's servers completely
  2. B.Certificates cannot send pushes to iPads
  3. C.One key works for all the team's apps and does not expire yearly
  4. D.A .p8 key makes pushes arrive with higher priority
Show answer

Answer: C. One key works for all the team's apps and does not expire yearly

Token-based authentication signs requests to APNs with a key that does not expire and can be used for every app in the team, in both sandbox and production. Certificates are per app and must be renewed yearly, and an expired one silently stops pushes. Keep the key secret like a password.

26. Card number in the app switcher

The iOS app switcher shows a snapshot of your app with the user's card number visible. How do you prevent this?

  1. A.Turn off multitasking support for the whole app
  2. B.Lower the screen brightness when backgrounded
  3. C.Delete the card number from the server
  4. D.Cover sensitive views when the scene resigns active
Show answer

Answer: D. Cover sensitive views when the scene resigns active

iOS takes a snapshot of the UI when the app moves to the background, and shows it in the app switcher. Placing a blur or privacy view when the scene resigns active (sceneWillResignActive) and removing it on becoming active hides sensitive data. Android uses FLAG_SECURE for the same purpose.

27. TestFlight internal and external testers

A team wants 15 colleagues and 2,000 beta users to try an iOS build. What is the difference between TestFlight internal and external testing?

  1. A.External testers need no review; internal builds need full App Review
  2. B.Both groups need a full App Store review before every build
  3. C.Internal testers need no review; external builds need Beta App Review first
  4. D.Internal testing is limited to 2 people and needs a paid account each
Show answer

Answer: C. Internal testers need no review; external builds need Beta App Review first

Internal testers are App Store Connect users on your team (up to 100) and can install builds as soon as they process. External testers (up to 10,000) join by email or public link, and the first build of a version must pass Beta App Review. TestFlight builds expire after 90 days.

28. Upload rejected for the same version

An iOS team uploads version 2.4.0 (build 57), finds a bug, fixes it and uploads again as 2.4.0 (build 57). App Store Connect rejects it. Why?

  1. A.The marketing version must change on every single upload
  2. B.Each upload for a version needs a higher CFBundleVersion build number
  3. C.Builds can only be uploaded once per calendar day
  4. D.The bundle identifier must change for every fix
Show answer

Answer: B. Each upload for a version needs a higher CFBundleVersion build number

CFBundleShortVersionString (2.4.0) is the version users see; CFBundleVersion (57) identifies the build. App Store Connect requires a new, higher build number for each upload of the same version. CI often sets it from the pipeline run number.

29. Reading an associated value

What does this print?

enum Payment {
    case success(amount: Int)
    case failure(String)
}

let p = Payment.failure("timeout")
switch p {
case .success: print("Success")
case .failure(let reason): print("Failed: \(reason)")
}
  1. A.Success
  2. B.Failed: timeout
  3. C.failure(timeout)
  4. D.It does not compile
Show answer

Answer: B. Failed: timeout

Enum cases can carry associated values. In a switch, case .failure(let reason) binds the value to a constant, so the print uses "timeout". This lets one type model different outcomes with their own data.

30. Floating-point money errors

An invoice adds 0.1 + 0.2 for GST calculations and shows 0.30000000000000004. What type is better for money?

  1. A.Float, because it uses less memory
  2. B.Double with more decimal places
  3. C.String, converted when adding
  4. D.Decimal, or integers in paise
Show answer

Answer: D. Decimal, or integers in paise

Double uses binary fractions, so many decimal values cannot be stored exactly. Decimal stores base-10 numbers, and many teams store money as integer paise or fils. Format with a currency-aware NumberFormatter or FormatStyle.

31. Adding past Int8.max

What happens when this code runs?

var x: Int8 = 127
x += 1
  1. A.x silently wraps around to −128
  2. B.It traps at runtime with an overflow
  3. C.x stays clamped at its max of 127
  4. D.x becomes 128, promoted to an Int
Show answer

Answer: B. It traps at runtime with an overflow

Swift arithmetic operators check for overflow and trap rather than wrapping silently. If wrapping is wanted, use &+ and the other overflow operators, or addingReportingOverflow.

32. Getting the first character

Why does name[0] fail to compile for a String?

  1. A.Strings are immutable, so indexing is blocked
  2. B.Strings use String.Index, because characters vary in size
  3. C.Only NSString supports any indexing
  4. D.Subscripts need a Character argument
Show answer

Answer: B. Strings use String.Index, because characters vary in size

A Character can be several Unicode scalars, so integer offsets cannot be computed in constant time. Use name.first, name.prefix(1), or name[name.index(name.startIndex, offsetBy: n)].

33. Parsing numbers from text

What is the value of result after this runs?

let result = ["1", "x", "3"].compactMap { Int($0) }
  1. A.[1, 3]
  2. B.[1, nil, 3]
  3. C.[1, 0, 3]
  4. D.[]
Show answer

Answer: A. [1, 3]

compactMap applies the transform and keeps only non-nil results. Int("x") is nil, so it is removed. map would give [Int?], and flatMap is for flattening nested sequences.

34. Two async let calls

fetchProfile() takes 1 s and fetchOrders() takes 2 s. How long does loadScreen() take?

func loadScreen() async throws -> Screen {
    async let profile = fetchProfile()
    async let orders = fetchOrders()
    return try await Screen(profile: profile, orders: orders)
}
  1. A.About 3 s
  2. B.About 1 s
  3. C.About 2 s
  4. D.About 0.5 s
Show answer

Answer: C. About 2 s

async let starts each child task immediately, and they run concurrently. Awaiting both waits for the slower one, so the total is about 2 s. Awaiting each call in turn without async let would take about 3 s.

35. Calling into an actor

Code outside the actor reads counter.value. Why does the compiler require await?

  1. A.Every property read in Swift needs await
  2. B.Actors always run only on the main thread
  3. C.Access may wait for the actor to be free
  4. D.The value is always fetched from the disk
Show answer

Answer: C. Access may wait for the actor to be free

An actor protects its state by running one task at a time. From outside, access to its isolated state is asynchronous and may suspend until the actor is free, so it needs await.

36. Showing SwiftData results

In SwiftData, what does @Query var notes: [Note] in a view do?

  1. A.Fetches notes and refreshes the view on changes
  2. B.Fetches once at launch and then never again
  3. C.Sends the query off to a remote server
  4. D.Creates a separate new database for each view
Show answer

Answer: A. Fetches notes and refreshes the view on changes

@Query uses the model context from the environment to fetch @Model objects and keeps the view in sync with inserts, edits and deletes. It accepts filters and sort descriptors.

37. Wrapping a UIKit view in SwiftUI

You wrap a MKMapView with UIViewRepresentable and need its delegate callbacks. Where do they go?

  1. A.Directly inside the SwiftUI body
  2. B.In a global singleton delegate object
  3. C.In the app’s Info.plist configuration
  4. D.A Coordinator from makeCoordinator()
Show answer

Answer: D. A Coordinator from makeCoordinator()

makeUIView creates the view once, updateUIView applies SwiftUI state changes, and a Coordinator object handles delegate callbacks and can push changes back through bindings.

38. Saving a draft when the app leaves

You want to save a draft when the app goes to the background. What do you observe in SwiftUI?

  1. A.The view’s body being called for the last time
  2. B.A timer that runs every second
  3. C.@Environment(\.scenePhase) changing to .background
  4. D.onDisappear of the root view
Show answer

Answer: C. @Environment(\.scenePhase) changing to .background

scenePhase reports .active, .inactive and .background. Reacting with onChange(of: scenePhase) lets you save state before the system may suspend or terminate the app.

39. Swift Testing assertions

In the Swift Testing framework, how do you write a test and check a value?

  1. A.Subclass XCTestCase and use print(total)
  2. B.Write a main() that returns true or false
  3. C.Use assert(total == 400) in production code
  4. D.Mark a func with @Test and use #expect(total == 400)
Show answer

Answer: D. Mark a func with @Test and use #expect(total == 400)

Swift Testing uses @Test functions and the #expect macro, which records failures with the evaluated values. #require stops the test if a condition fails. It can run alongside XCTest.

40. Combine sink never fires

A Combine subscription is created with sink in a method, but the closure never runs. What is missing?

  1. A.Storing the returned AnyCancellable
  2. B.Calling sink twice so it activates
  3. C.Marking the method with @MainActor
  4. D.Adding receive(on:) after the sink
Show answer

Answer: A. Storing the returned AnyCancellable

sink returns an AnyCancellable that cancels the subscription when it is deallocated. If you do not keep it, the subscription ends right away. Store it with .store(in: &cancellables).

iOS interview questions for experienced candidates hard

41. Memory leak with a closure

This view controller is never deallocated after it is dismissed. Why?

class ProfileVC: UIViewController {
  var onUpdate: (() -> Void)?
  override func viewDidLoad() {
    super.viewDidLoad()
    onUpdate = {
      self.reload()
    }
  }
  func reload() {}
}
  1. A.viewDidLoad is called twice
  2. B.Optionals always leak memory in Swift
  3. C.reload is not marked @objc
  4. D.The VC strongly holds the closure and the closure strongly captures self, creating a retain cycle; use [weak self]
Show answer

Answer: D. The VC strongly holds the closure and the closure strongly captures self, creating a retain cycle; use [weak self]

Swift uses ARC. The controller owns onUpdate, and the closure captures self strongly, so each keeps the other alive and the reference count never reaches zero. Capturing [weak self] (or unowned when lifetime is guaranteed) breaks the cycle. Xcode Memory Graph Debugger can show such cycles.

42. Actor reentrancy in an image cache

Two callers ask this actor for the same URL at the same moment. What happens?

actor ImageCache {
  private var cache: [URL: UIImage] = [:]

  func image(for url: URL) async throws -> UIImage {
    if let cached = cache[url] { return cached }
    let image = try await download(url)
    cache[url] = image
    return image
  }
}
  1. A.The actor runs one call at a time from start to finish, so the second caller waits until the first returns and then finds the image already in the cache
  2. B.A data race on the dictionary crashes the app
  3. C.It does not compile, because actors cannot have async methods
  4. D.Both calls may download, because actors are reentrant: while the first call awaits, the second runs and also misses the cache; store in-flight Tasks in the dictionary to share one download
Show answer

Answer: D. Both calls may download, because actors are reentrant: while the first call awaits, the second runs and also misses the cache; store in-flight Tasks in the dictionary to share one download

Actors protect their state from simultaneous access, but at every await they can run other queued calls. The second call runs while the first is suspended in download, sees no cached image and starts its own download. Caching a Task<UIImage, Error> per URL lets later callers await the same in-flight work.

43. Keychain read fails in background

A background refresh task runs while the iPhone is locked, reads the auth token from the Keychain and gets errSecInteractionNotAllowed. What is the cause?

  1. A.The Keychain cannot be used by background code at all, so the token should be copied into UserDefaults before the app is suspended so background tasks can read it
  2. B.The token was saved with kSecAttrAccessibleWhenUnlocked; items needed in the background should use kSecAttrAccessibleAfterFirstUnlock (the ThisDeviceOnly variant if it should not move to other devices)
  3. C.Background tasks have no network access
  4. D.The token has expired on the server
Show answer

Answer: B. The token was saved with kSecAttrAccessibleWhenUnlocked; items needed in the background should use kSecAttrAccessibleAfterFirstUnlock (the ThisDeviceOnly variant if it should not move to other devices)

Keychain items are protected by data protection classes tied to the device lock state. WhenUnlocked items cannot be read while the device is locked, so background work fails. AfterFirstUnlock keeps the item readable after the first unlock since boot, which is the usual choice for tokens used in background tasks.

44. When a BGAppRefreshTask runs

You submit a BGAppRefreshTaskRequest with earliestBeginDate set to 15 minutes from now. When will the task run?

  1. A.Exactly 15 minutes from now
  2. B.Every 15 minutes for as long as the app is installed, because a submitted request repeats on that interval until you cancel it with the scheduler
  3. C.Immediately, and again after 15 minutes
  4. D.No earlier than 15 minutes from now; the system chooses the time based on usage patterns, battery and network, and it may run much later or not at all
Show answer

Answer: D. No earlier than 15 minutes from now; the system chooses the time based on usage patterns, battery and network, and it may run much later or not at all

earliestBeginDate is a lower bound, not a schedule. iOS runs refresh tasks when it predicts the user will open the app soon and conditions are good, and rarely-used apps get fewer chances. Each run must schedule the next request, and critical sync should also happen when the app comes to the foreground.

45. App killed during a slow launch

An iOS app downloads config synchronously in didFinishLaunching. On slow networks, the system kills it before the first screen appears. Why?

  1. A.iOS forbids any network calls during the app launch phase
  2. B.A watchdog kills apps that block the main thread at launch
  3. C.The app ran out of free disk space for the download
  4. D.Synchronous code is not allowed in Swift apps
Show answer

Answer: B. A watchdog kills apps that block the main thread at launch

iOS uses a watchdog that terminates apps that do not finish launching or stay unresponsive for too long, with crash logs showing the 0x8badf00d code. Launch should show UI fast and load remote config asynchronously, using cached or default values meanwhile.

46. Core Data objects across threads

An iOS app fetches NSManagedObjects on a background context and passes them to the main thread for display. It crashes randomly. What is the rule?

  1. A.Managed objects are tied to their context; pass objectIDs instead
  2. B.Core Data can only be used from background threads
  3. C.Each fetch must create a new persistent store file
  4. D.Managed objects must be converted to JSON first
Show answer

Answer: A. Managed objects are tied to their context; pass objectIDs instead

An NSManagedObject belongs to the context that loaded it and must be used only on that context's queue (via perform). To hand data across, pass the NSManagedObjectID and fetch it in the other context. SwiftData has similar rules with ModelContext and model actors.

47. Large downloads in Documents on iOS

An iOS app stores 2 GB of re-downloadable videos in the Documents directory. Why might Apple reject it?

  1. A.Documents is limited to 100 MB per app
  2. B.Videos can only be stored in the Photos library
  3. C.Documents is shared with every other app on the phone
  4. D.Documents is backed up to iCloud; re-downloadable data must not be
Show answer

Answer: D. Documents is backed up to iCloud; re-downloadable data must not be

Apple's data storage rules say user-created content goes in Documents, which is backed up. Content that can be downloaded again belongs in Caches, or must be marked with isExcludedFromBackup. Large backed-up data fills users' iCloud storage and is a known rejection reason.

48. Downloads that continue after the app closes

An iOS app downloads 500 MB course videos. Users switch apps and the download stops. Which API lets the system continue the transfer?

  1. A.A Timer that restarts the download every 30 seconds
  2. B.A background URLSessionConfiguration with a download task
  3. C.A default URLSession on a background DispatchQueue
  4. D.A silent push notification sent every minute
Show answer

Answer: B. A background URLSessionConfiguration with a download task

A background URLSession hands the transfer to a system daemon, which keeps downloading while the app is suspended or even terminated. When it finishes, iOS wakes the app to handle the result through the session delegate. Normal sessions stop when the app is suspended.

49. Extension method that is not a requirement

What does this print?

protocol Greeter {}
extension Greeter {
    func hello() -> String { "base" }
}
struct Hindi: Greeter {
    func hello() -> String { "namaste" }
}

let g: any Greeter = Hindi()
print(g.hello())
  1. A.namaste
  2. B.base
  3. C.It does not compile
  4. D.base namaste
Show answer

Answer: B. base

hello() is defined only in the protocol extension, not as a requirement. Calls through an any Greeter value are dispatched statically to the extension. If hello() were declared in the protocol, the call would use dynamic dispatch and print "namaste".

50. some Shape or any Shape

Which statement about some Shape and any Shape is correct?

  1. A.some can hold different types at runtime; any fixes one
  2. B.any can hold different conforming types; some fixes one type
  3. C.They are identical; any is only newer syntax
  4. D.any is only allowed for class protocols
Show answer

Answer: B. any can hold different conforming types; some fixes one type

some is an opaque type: one specific concrete type the compiler knows. any is an existential box that can hold any conforming value, with some dynamic cost. Prefer some or generics, and use any for heterogeneous collections.

51. Typed throws

With Swift 6 typed throws, what is the type of error in the catch block?

func pay() throws(PaymentError) { /* ... */ }

do {
    try pay()
} catch {
    // error’s type here?
}
  1. A.any Error (existential)
  2. B.Optional<Error>
  3. C.NSError from Foundation
  4. D.PaymentError
Show answer

Answer: D. PaymentError

throws(PaymentError) declares the exact error type, so a do/catch around only that call gets error as PaymentError without casting. Plain throws is the same as throws(any Error). Typed throws suits closed error domains and embedded code.

52. Counting an emoji

What does this print?

let s = "👍🏽"
print(s.count, s.utf16.count)
  1. A.2 4
  2. B.1 4
  3. C.1 2
  4. D.4 4
Show answer

Answer: B. 1 4

Swift’s count counts grapheme clusters, so 👍🏽 (thumbs up plus skin tone modifier) is one Character. In UTF-16 it is two scalars of two code units each, so utf16.count is 4. This matters for text limits and NSRange.

53. Turning a delegate into an AsyncSequence

You want to expose location updates from a delegate-based API as for await. What is the usual tool?

  1. A.AsyncStream with a continuation that yields
  2. B.A single withCheckedContinuation call
  3. C.DispatchGroup with paired enter and leave
  4. D.A Timer that fires once every second
Show answer

Answer: A. AsyncStream with a continuation that yields

AsyncStream gives a continuation you can yield to from callbacks and finish when done. Set onTermination to stop the underlying updates when the consumer cancels. A checked continuation suits only one result.

54. What the $ prefix gives

For a property declared with a property wrapper, what does the $ prefix (as in $isOn for @State var isOn) access?

  1. A.The wrapper’s stored value as an optional
  2. B.The memory address of the property
  3. C.A copy of the property made at init
  4. D.The wrapper’s projectedValue, for @State a Binding
Show answer

Answer: D. The wrapper’s projectedValue, for @State a Binding

A property wrapper exposes wrappedValue (used by the plain name) and can expose projectedValue through $. @State projects a Binding, and @Published projects a Combine publisher.

55. Rows showing the wrong data

A List uses ForEach(names, id: \.self). When two people share the name "Priya", editing one row updates the other. Why?

  1. A.id: \.self only works for Int arrays
  2. B.Duplicate values give two rows the same identity
  3. C.List always mixes up rows with strings
  4. D.Text views cannot show duplicate strings
Show answer

Answer: B. Duplicate values give two rows the same identity

SwiftUI uses the id to track rows. With \.self, equal values share an identity, which confuses diffing, state and animations. Use a model with a unique stable ID, such as a UUID or database key.

56. sync on the main queue

What happens when this code runs on the main thread?

DispatchQueue.main.sync {
    label.text = "Done"
}
  1. A.The block runs immediately, then the rest
  2. B.The block runs later on a background thread
  3. C.It deadlocks; GCD usually traps with a crash
  4. D.The block is ignored without any error
Show answer

Answer: C. It deadlocks; GCD usually traps with a crash

sync waits for the block to finish on the target queue. The main queue is serial and is busy running this very code, so the block can never start. libdispatch detects this and crashes. Use async, or call the code directly.

57. App rejected for UserDefaults use

App Store Connect flags your app for using UserDefaults and file timestamp APIs without declared reasons. What is required?

  1. A.A PrivacyInfo.xcprivacy declaring reasons for those APIs
  2. B.Removing every use of UserDefaults from the app
  3. C.Asking users for App Tracking Transparency consent
  4. D.Adding a privacy policy URL in App Store Connect
Show answer

Answer: A. A PrivacyInfo.xcprivacy declaring reasons for those APIs

Apple’s privacy manifest rules require apps and SDKs to declare "required reason" APIs, such as UserDefaults, file timestamps, system boot time and disk space, with an approved reason code. Third-party SDKs need their own manifests too.

58. @State ignores the new value

A view initialises @State from a parameter. The parent later passes a different start value, but the view keeps showing the old one. Why?

struct Stepper: View {
    @State private var count: Int
    init(start: Int) { _count = State(initialValue: start) }
    var body: some View { Text("\(count)") }
}
  1. A.Parameters cannot be passed to views with state
  2. B.@State is set only when the view’s identity is first created
  3. C.The parent must call objectWillChange.send()
  4. D.State values reset only on device rotation
Show answer

Answer: B. @State is set only when the view’s identity is first created

SwiftUI keeps @State storage for the life of the view’s identity and ignores later initial values. Use a @Binding or plain property if the parent owns the value, or change the view’s .id() to reset it on purpose.

59. Which views update with @Observable

A model marked @Observable has name and cartCount. A header view reads only name. When cartCount changes, does the header’s body run again?

  1. A.Yes; any change to the model updates every view
  2. B.Yes, but only if the view uses @StateObject
  3. C.No; views track only the properties they read
  4. D.No; @Observable models never update views
Show answer

Answer: C. No; views track only the properties they read

The Observation framework records which properties a view’s body accessed and invalidates only on changes to those. ObservableObject with @Published, by contrast, notifies every observer on any change, which can cause extra updates.

60. Search that restarts on new text

A view uses .task(id: query) { await search(query) }. What happens when query changes while a search is running?

  1. A.Both searches run and both update the view
  2. B.The new query is ignored until the old ends
  3. C.The view crashes with a concurrency error
  4. D.The running task is cancelled and a new one starts
Show answer

Answer: D. The running task is cancelled and a new one starts

.task(id:) ties the task to a value. When the value changes, SwiftUI cancels the old task and starts a new one, and it also cancels when the view disappears. The search code must respect cancellation for this to stop work.

Ready to test yourself?

Take a 15-question iOS MCQ test with a score at the end, or practise the full iOS question bank and earn points on the leaderboard.