Bird
Raised Fist0
Swiftprogramming~5 mins

Testing async code in Swift - Time & Space Complexity

Choose your learning style10 modes available
Time Complexity: Testing async code
O(n)
Understanding Time Complexity

When testing async code, we want to know how the time to complete grows as the tasks increase.

We ask: How does waiting for asynchronous tasks affect overall execution time?

Scenario Under Consideration

Analyze the time complexity of the following async code snippet.


func fetchData(ids: [Int]) async {
  for id in ids {
    let data = await fetchFromNetwork(id: id)
    print(data)
  }
}

func fetchFromNetwork(id: Int) async -> String {
  // Simulates network delay
  try? await Task.sleep(nanoseconds: 1_000_000_000)
  return "Data for \(id)"
}
    

This code fetches data for each id one after another, waiting for each network call to finish before starting the next.

Identify Repeating Operations

Identify the loops, recursion, array traversals that repeat.

  • Primary operation: The for-loop that calls fetchFromNetwork for each id.
  • How many times: Once for each element in the input array ids.
How Execution Grows With Input

Each network call waits about 1 second before returning. Since calls happen one after another, total time adds up.

Input Size (n)Approx. Operations (seconds)
10~10 seconds
100~100 seconds
1000~1000 seconds

Pattern observation: Time grows linearly as the number of ids increases because each call waits fully before the next starts.

Final Time Complexity

Time Complexity: O(n)

This means the total time grows directly in proportion to the number of async calls made one after another.

Common Mistake

[X] Wrong: "Async calls always run at the same time, so total time stays constant no matter how many calls."

[OK] Correct: In this code, calls happen one after another because of the await inside the loop, so times add up instead of overlapping.

Interview Connect

Understanding how async code runs step-by-step helps you explain performance clearly and shows you can reason about real-world delays and waiting times.

Self-Check

What if we changed the code to start all fetches at once and then wait for all to finish? How would the time complexity change?

Practice

(1/5)
1. What keyword must you add to a Swift test function to test asynchronous code?
easy
A. defer
B. await
C. async
D. try

Solution

  1. Step 1: Understand async test function requirement

    To test asynchronous code, the test function itself must be marked as asynchronous.
  2. Step 2: Identify the correct keyword

    The keyword to mark a function as asynchronous in Swift is async.
  3. Final Answer:

    async -> Option C
  4. Quick Check:

    async = async [OK]
Hint: Mark test functions with async to test async code [OK]
Common Mistakes:
  • Using await instead of async on test function
  • Forgetting to mark test function as async
  • Using try instead of async
  • Confusing defer with async
2. Which of the following is the correct syntax to wait for an async function fetchData() inside a test?
easy
A. let result = await fetchData()
B. let result = async fetchData()
C. await let result = fetchData()
D. fetchData().await()

Solution

  1. Step 1: Recall await usage in Swift

    In Swift, to wait for an async function, you use the await keyword before the function call.
  2. Step 2: Check correct syntax

    The correct syntax is let result = await fetchData(). The keyword await comes before the function call.
  3. Final Answer:

    let result = await fetchData() -> Option A
  4. Quick Check:

    await before function call = let result = await fetchData() [OK]
Hint: Put await before async function call to wait [OK]
Common Mistakes:
  • Placing await after let or variable
  • Using async instead of await before call
  • Calling await as a method
  • Omitting await keyword
3. Given this test code, what will be printed?
func testFetch() async {
  let data = try? await fetchData()
  print(data == nil ? "No Data" : "Got Data")
}

func fetchData() async throws -> String {
  return "Hello"
}
medium
A. No Data
B. Compilation error
C. Runtime error
D. Got Data

Solution

  1. Step 1: Analyze fetchData() behavior

    The function fetchData() is async and throws but always returns "Hello" without throwing an error.
  2. Step 2: Understand try? await usage

    The try? await fetchData() will return the string "Hello" wrapped as optional, not nil, because no error is thrown.
  3. Step 3: Evaluate print statement

    Since data is not nil, the print outputs "Got Data".
  4. Final Answer:

    Got Data -> Option D
  5. Quick Check:

    try? await returns value = Got Data [OK]
Hint: try? returns nil only if error thrown, else returns value [OK]
Common Mistakes:
  • Assuming try? always returns nil
  • Confusing async throws with throwing error always
  • Ignoring optional unwrapping
  • Expecting compilation error due to async
4. Identify the error in this async test function:
func testDownload() async {
  let result = fetchData()
  XCTAssertEqual(result, "Success")
}

func fetchData() async -> String {
  return "Success"
}
medium
A. XCTAssertEqual cannot compare strings
B. Missing await before fetchData() call
C. Test function should not be async
D. fetchData() must throw an error

Solution

  1. Step 1: Check async function call

    The function fetchData() is async, so calling it requires await to get the result.
  2. Step 2: Identify missing await

    In the test, fetchData() is called without await, causing a compile error.
  3. Final Answer:

    Missing await before fetchData() call -> Option B
  4. Quick Check:

    Async calls need await = Missing await before fetchData() call [OK]
Hint: Always await async calls inside async functions [OK]
Common Mistakes:
  • Calling async function without await
  • Thinking test functions can't be async
  • Misusing XCTAssertEqual with strings
  • Assuming async functions must throw
5. You want to test an async function loadUser(id:) that throws an error if the user is not found. Which test code correctly checks that an error is thrown for an invalid id?
hard
A. func testLoadUserError() async throws { do { _ = try await loadUser(id: -1) XCTFail("Expected error not thrown") } catch { XCTAssertTrue(true) } }
B. func testLoadUserError() async { let user = try await loadUser(id: -1) XCTAssertNil(user) }
C. func testLoadUserError() async throws { let user = await loadUser(id: -1) XCTAssertNil(user) }
D. func testLoadUserError() async throws { _ = try await loadUser(id: -1) XCTAssertTrue(false) }

Solution

  1. Step 1: Understand error throwing test pattern

    To test that an async function throws, use a do-catch block and call the function with try await.
  2. Step 2: Verify correct error handling

    func testLoadUserError() async throws { do { _ = try await loadUser(id: -1) XCTFail("Expected error not thrown") } catch { XCTAssertTrue(true) } } calls loadUser(id: -1) with try await inside do, fails test if no error, and passes if error caught.
  3. Step 3: Check other options

    Approaches expecting a nil user incorrectly assume throwing functions return nil instead of throwing. func testLoadUserError() async throws { _ = try await loadUser(id: -1) XCTAssertTrue(false) } fails test unconditionally.
  4. Final Answer:

    do { _ = try await loadUser(id: -1) XCTFail("Expected error not thrown") } catch { XCTAssertTrue(true) } -> Option A
  5. Quick Check:

    Use do-catch with try await to test errors [OK]
Hint: Use do-catch with try await to test thrown errors [OK]
Common Mistakes:
  • Expecting nil instead of catching error
  • Omitting try or await keywords
  • Failing test unconditionally
  • Not marking test async throws
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard