Sitemap
Trendyol Tech

Trendyol Tech Team

The Scroll Problem in iOS UI Tests and How XCScrollHelper Solves It

5 min readJul 30, 2026

--

Press enter or click to view image in full size

If you asked most iOS developers what wastes the most time when writing UI tests, many would give the same answer: scrolling. Reaching an off-screen element, tapping it, verifying it’s in the right position… It sounds simple, but in practice it’s a real engineering problem.

In this post, we’ll walk through how we hit this problem on the Trendyol iOS team, why existing solutions fell short, and how we solved it with XCScrollHelper. The library was built by Mine Oz at Trendyol, and we worked together to open-source it for the community.

The Problem

In an e-commerce app, screens are typically made up of long, scrollable lists. Campaign banners, product carousels, category grids, recommendation sections, all stacked inside a single UICollectionView or UIScrollView, often well below the visible area.

For snapshot tests, we need to verify these screens in different states. That means scrolling to the relevant component and capturing the screen at that point. With the standard XCTest approach, this is what it looks like:

let maxSwipes = 30
var swipeCount = 0
while !targetCell.exists && swipeCount < maxSwipes {
collectionView.swipeUp(velocity: .fast)
swipeCount += 1
}

Does this work? Yes, most of the time. But it has a few critical issues:

1. It’s slow. Each swipeUp() call requires XCTest to simulate an animation, wait for the UI to settle, and re-scan the accessibility tree. A single swipe takes roughly 0.5 to 1 second. If your target is 20 swipes away, the scroll alone takes 10 to 20 seconds.

2. It’s unreliable. Swipe distance varies depending on device size, dynamic type settings, and cell height. An element that takes 15 swipes to find today might need 20 after a layout change. You have to set maxSwipes high enough to cover the worst case, which makes the test even slower.

3. There’s no precise positioning. Even after finding the element, you can’t control exactly where it sits on screen. For snapshot tests, this matters: whether a component appears at the top, center, or bottom of the viewport determines whether the test passes or fails.

What Does XCScrollHelper Do?

XCScrollHelper completely bypasses XCTest’s swipe-loop mechanics. Instead, it establishes file-based communication between the app process and the test process, and performs the scroll programmatically.

Here’s how it works:

  1. The UI test creates a JSON file inside the simulator’s shared resources directory and writes the target element’s accessibility identifier and desired scroll position into it.
  2. A FileMonitor on the app side detects the file change.
  3. ComponentScrollHelper uses the UICollectionView's layout attributes to calculate the exact coordinates of the target element.
  4. It calls setContentOffset to scroll directly to that point: no swipe animation, no waiting.
  5. The app writes the result (success/failure) back to the same file.
  6. The test side polls the file, reads the result, and continues.

The key insight: we never touch XCTest’s scroll mechanism. The app calls setContentOffset internally, which completes in milliseconds.

The Numbers

The Trendyol iOS project currently has 2,889 snapshot test methods across 528 test files. Every single one of them opens a page via deeplink, scrolls to a specific component, and captures the screen.

With the classic swipe-loop approach, a single scroll operation takes an average of 25 seconds. With XCScrollHelper, the same operation takes 5 seconds. That’s a 5x improvement.

Get Dogancan Mavideniz’s stories in your inbox

Join Medium for free to get updates from this writer.

Scale that across the entire test suite and the savings add up fast. In CI pipelines that run dozens of times a day, this translates directly into faster feedback for developers on their pull requests.

Usage

App Side

Start the observer in your AppDelegate:

import XCScrollHelper

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UITestAutomationObserver.shared.start() // CollectionView support
UITestAutomationObserver.shared.startForScrollView() // ScrollView support
return true
}

Test Side

One line instead of a swipe loop:

import XCScrollHelper

// Classic approach: fragile and slow
let maxSwipes = 30
var swipeCount = 0
while !targetCell.exists && swipeCount < maxSwipes {
collectionView.swipeUp(velocity: .fast)
swipeCount += 1
}

// XCScrollHelper: fast and reliable
let result = XCScrollTestHelper.shared.scrollToCell(
elementIdentifier: "campaign_banner",
in: "homeCollectionView",
position: .top
)
XCTAssertTrue(result.isSuccess)

It handles section headers, footers, and horizontal collection views just as easily:

// Scroll to a section header
XCScrollTestHelper.shared.scrollToCell(
elementIdentifier: "header_recommendations",
in: "homeCollectionView",
position: .top,
type: .elementKindSectionHeader
)
// Scroll to a cell in a horizontal collection view
XCScrollTestHelper.shared.scrollToCell(
elementIdentifier: "product_card_42",
in: "horizontalCarousel",
position: .leading
)
// Scroll to a subview inside a plain UIScrollView
XCScrollTestHelper.shared.scrollToView(
targetIdentifier: "address_form",
in: "checkoutScrollView"
)

Why Programmatic Scroll Instead of Swipes?

This distinction is worth expanding on, because “why not just use XCTest’s built-in scrolling?” is a natural question.

In XCTest, scrolling goes through the UI layer. The framework simulates a real user interaction: finger down, drag, finger up. At each step, the accessibility tree updates, animations complete, and layout passes run. This is the right approach for functional tests, because you’re testing what the user actually experiences.

But snapshot tests have a different goal. We’re not testing the scroll experience. We’re verifying how a specific component looks in a specific data state. Scrolling is a means to an end, not the test itself. Spending 25 seconds simulating a realistic scroll adds no value here.

XCScrollHelper uses UICollectionViewLayout's layoutAttributesForItem(at:) to get the target cell's frame directly from the layout engine. Then it moves the collection view to those coordinates with setContentOffset. No animation, no swipe simulation. That's why it's fast.

Built-in Retry Logic

In the real world, things don’t always work on the first try. Cells might not be loaded yet, the data source might not have updated, the layout might not have been calculated. XCScrollHelper accounts for this with a built-in retry mechanism.

When ComponentScrollHelper can't find the target element, it scrolls forward through the visible cells and keeps looking. It retries up to 9 times before giving up. This makes it reliable even with collection views that load data lazily.

What It Changed for Us

After integrating XCScrollHelper into the Trendyol iOS project, here’s what we observed:

  • Test durations dropped. An average 5x speedup on tests that involve scrolling. Saving 20 seconds per test adds up across the suite.
  • Flaky test rate decreased. Tests that depended on swipe counts were affected by device and layout changes. Programmatic scrolling eliminated that variable.
  • Writing tests became easier. Developers no longer need boilerplate scroll code. A single method call gets them to the component they need.
  • CI feedback got faster. Faster tests mean a shorter PR review cycle.

Conclusion

Scrolling in UI tests is more expensive than it looks. Especially in scenarios like snapshot testing, where the scroll itself doesn’t matter and only the result does, the classic swipe-loop approach introduces unnecessary slowness and fragility.

XCScrollHelper solves this at the root by establishing file-based communication between the app and test processes. We actively use it at Trendyol across all 2,889 snapshot tests, and it has become a critical part of our test infrastructure.

The project is open source under the MIT license: github.com/Trendyol/XCScrollHelper

About Us

We’re building a team of the brightest minds in our industry. Interested in joining us? Visit the pages below to learn more about our open positions.

Trendyol Careers

--

--