Skip to content

[scroll] replace ScrollViewType with UIKit's scroll view names on ScrollView - #105

Merged
honghaoz merged 2 commits into
masterfrom
scroll/uikit-named-adapter
Oct 1, 2026
Merged

honghaoz merged 2 commits into
masterfrom
scroll/uikit-named-adapter

Conversation

@honghaoz

@honghaoz honghaoz commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • ScrollView is now a UIKit-named class adapter: one scroll view API on both platforms, with UIKit's names and meanings, and no name that means two things on macOS.
  • Removes ScrollViewType and its method-style accessors, which reused NSScrollView's own names (bounds, contentSize, contentView) with different meanings. For contentView(), that let Swift silently pick AppKit's clip view in an optional or inferred context (AppKit: contentView() resolves to NSScrollView.contentView (the clip view) in optional or inferred contexts #27).
  • Renames isScrollable to UIKit's isScrollEnabled.

What changed

  • AppKit ScrollView gains contentOffset, contentInset, adjustedContentInset, visibleSize and isScrollEnabled, implemented on the clip view. On iOS these are UIScrollView's own. contentSize keeps its existing override, which AppKit itself never reads (probed in tiling, layout, display, scrolling, resizing, scroller changes and magnification).
  • Helpers (minOffsetX/Y, maxOffsetX/Y, canScrollTo*, stopDecelerating()) move to ScrollView+Scrolling.swift, rewritten on the new names.
  • Renderable container: the internal contentContainerView, the document view on macOS and the scroll view itself on iOS, replaces contentView() and the public documentView().
  • ComposeView and the slide transition use the new members. The render pass assigns self.contentSize, since a local variable shadows the property there.
  • Tests: about 175 call sites migrated. ScrollViewTypeTests is split into ScrollViewTests, now cross-platform contract tests, and ScrollView+ScrollingTests.
  • CHANGELOG: two Breaking Changes entries list every replacement, including how to keep setBounds(_:)'s behavior on each platform.

Platform differences kept on purpose

Each is pinned by a test, with a follow-up issue:

Test plan

  • Full macOS suite: 1,391 tests, 0 failures. One of five local runs had a single failure whose output I didn't capture, and it didn't reproduce in the next four runs. The new scroll view tests passed 20 of 20 in a loop.
  • Full iOS suite: 1,305 tests, 0 failures, locally and in CI.
  • tvOS and visionOS pass in CI. The gate test that UIKit's visibleSize equals bounds.size, with a content inset and zoom, passes on iOS, tvOS and visionOS. On visionOS, AnimationClockTests.test_now_firstReadDuringTheCommit_theTurnEndsWhenTheLoopWakes failed once and passed on CI's retry. It's timing-sensitive and unrelated to this change.
  • Coverage: every new or changed line runs under the tests.
  • make format and make lint.

Follow-ups

#99, #100, #101, #102, #103, #104

Fixes #27

…ollView

`ScrollViewType` reused names `NSScrollView` already has (`bounds`,
`contentSize`, `contentView`) as same-named methods, which gave each name two
meanings on AppKit. For `contentView()` it was a trap: Swift imports a
`contentView() -> NSView?` onto every `NSScrollView` from
`NSTextFinderBarContainer`, so in an optional or inferred context a call
resolved to AppKit's clip view instead of the document view (#27). The
protocol added nothing either: `ScrollView` is its superclass constraint and
conforms itself, so no other type can conform.

`ScrollView` now has UIKit's names and meanings on both platforms. On AppKit
it implements `contentOffset`, `contentInset`, `adjustedContentInset`,
`visibleSize` and `isScrollEnabled` on top of the clip view, and keeps its
`contentSize` override, which AppKit itself never reads. The scroll range
helpers move to `ScrollView+Scrolling.swift`, and the renderable container
becomes the internal `contentContainerView`, which also replaces the public
`documentView()`.

Removed: `ScrollViewType`, `bounds()`, `setBounds(_:)`, `contentSize()`,
`setContentSize(_:)`, `contentInsets()`, `setContentInsets(_:)`,
`contentOffset()`, `setContentOffset(_:)`, `contentView()`, the macOS
`documentView()`, and `BaseScrollView.isScrollable`.

Fixes #27.
@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: b4d6d38a-c178-45a4-8871-c686ec8f8a6a

📥 Commits

Reviewing files that changed from the base of the PR and between 5c95ab4 and e15f3f5.

📒 Files selected for processing (42)
  • CHANGELOG.md
  • ComposeUI/Sources/ComposeUI/ComposeNode/RenderItem/RenderableTransition+Slide.swift
  • ComposeUI/Sources/ComposeUI/ComposeView/ComposeView+Debug.swift
  • ComposeUI/Sources/ComposeUI/ComposeView/ComposeView+ZOrder.swift
  • ComposeUI/Sources/ComposeUI/ComposeView/ComposeView.swift
  • ComposeUI/Sources/ComposeUI/CrossPlatform/ScrollView/BaseScrollView.swift
  • ComposeUI/Sources/ComposeUI/CrossPlatform/ScrollView/ScrollView+Scrolling.swift
  • ComposeUI/Sources/ComposeUI/CrossPlatform/ScrollView/ScrollView.swift
  • ComposeUI/Sources/ComposeUI/CrossPlatform/ScrollView/ScrollViewType.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/ButtonNodeTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/ColorNodeTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/ComposeViewNode+AnimationTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/ComposeViewNode+ParentResizeTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/ComposeViewNodeTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/DropShadowNodeTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/GestureRecognizerNodeTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/InnerShadowNodeTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/LabelNodeTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/LayerNodeTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/SwiftUIViewNode+ContentEvaluationTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/TextNodeTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeNodes/ViewNodeTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+AnimationBehaviorTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+AnimationDecisionTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+CachedLayoutTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+ContentUpdateContextTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+PreparedContentTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+ReentrantRefreshTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+RefreshTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+RenderBoundsTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+RenderFrameUpdateTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+RenderHandlerTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+RenderReuseTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+RenderableTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+RenderableUpdateBoundsTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+ScrollBehaviorTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeView+ZOrderTests.swift
  • ComposeUI/Tests/ComposeUITests/ComposeView/ComposeViewTests.swift
  • ComposeUI/Tests/ComposeUITests/CrossPlatform/ScrollView/ScrollView+ScrollingTests.swift
  • ComposeUI/Tests/ComposeUITests/CrossPlatform/ScrollView/ScrollViewTests.swift
  • ComposeUI/Tests/ComposeUITests/Performance/ModifierPerformanceTests.swift
  • ComposeUI/Tests/ComposeUITests/Performance/RenderPerformanceTests.swift
💤 Files with no reviewable changes (2)
  • ComposeUI/Sources/ComposeUI/CrossPlatform/ScrollView/BaseScrollView.swift
  • ComposeUI/Sources/ComposeUI/CrossPlatform/ScrollView/ScrollViewType.swift

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The pull request replaces ScrollViewType with a cross-platform ScrollView API. It updates scroll behavior and rendering code to use the new properties and container, and migrates tests to those APIs.

Changes

Cross-platform ScrollView API

Layer / File(s) Summary
ScrollView properties and scrolling behavior
CHANGELOG.md, ComposeUI/Sources/ComposeUI/CrossPlatform/ScrollView/*
ScrollViewType is removed. ScrollView adds content offset, insets, visible size, scroll-enabled state, and direction checks. BaseScrollView.isScrollable is removed.
ComposeView rendering and scroll behavior
ComposeUI/Sources/ComposeUI/ComposeNode/RenderItem/RenderableTransition+Slide.swift, ComposeUI/Sources/ComposeUI/ComposeView/*
ComposeView uses the new scroll properties and content container. Bottom and right slide offsets use visibleSize; the debug event label changes to isScrollEnabled.
ComposeView test migration
ComposeUI/Tests/ComposeUITests/ComposeNodes/*, ComposeUI/Tests/ComposeUITests/ComposeView/*, ComposeUI/Tests/ComposeUITests/Performance/*
Tests use the new scroll properties and content container. Existing assertions and scenarios are retained, with updated checks for visible size and platform-specific scrolling behavior.
Cross-platform ScrollView API tests
ComposeUI/Tests/ComposeUITests/CrossPlatform/ScrollView/*
Tests cover content offsets, sizes, insets, visible sizes, container identity, direction checks, and AppKit scroll-wheel handling.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature · Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to e15f3

This change moves ScrollView to a cross-platform property API and updates ComposeView and its tests to use it. No concrete defect was identified, and the author reports passing macOS and iOS test runs. tvOS and visionOS rely on CI.

Security Architecture Review

Security architecture risk: 🔵 Low · up to e15f3

This is a documented breaking API change within one UI package. The inspected paths retain existing content ownership, rendering order, and event routing. No material security regression was established, but downstream compatibility and complete security coverage remain unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The inspected input-to-outcome paths affect scroll state, renderable placement, and the enclosing application's responder hierarchy. They do not establish a new tenant, service, credential, or privileged-resource boundary crossing; downstream application handlers were not assessed.

Trust Boundaries and Controls

  • observed — On AppKit, disabled scrolling forwards wheel events to nextResponder before changing the local scroll session. This delegation previously occurred in BaseScrollView and now occurs in ScrollView; it is UI routing, not an authorization control.

Resilience and Maintainability Implications

  • observed — Scroll-session target selection and phase handling remain unchanged. Elasticity invalidation still coalesces repeated requests, schedules work on the main run loop, and weakly captures the owner. The migration does not add a new asynchronous ownership transfer or recovery mechanism.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.28% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 133 functions across 39 files. (1 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Issue #27 requires removal of the ambiguous contentView() resolution, explicit clip-view scrolling, migrated call sites, and platform-specific container coverage. The PR removes `ScrollViewType.cont…
Out of Scope Changes check ✅ Passed The broader API migration remains connected to issue #27. It removes the method-style ScrollViewType accessors that permit platform name collisions, replaces them with UIKit-named properties, update…
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: replacing ScrollViewType with UIKit-style scroll view names on ScrollView.
Full details: Docstring Coverage

Explanation

Docstring coverage is 11.28% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 133 functions across 39 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.99%. Comparing base (5c95ab4) to head (bd95085).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #105      +/-   ##
==========================================
+ Coverage   95.44%   95.99%   +0.54%     
==========================================
  Files         114      114              
  Lines        7006     6985      -21     
==========================================
+ Hits         6687     6705      +18     
+ Misses        319      280      -39     
Flag Coverage Δ
ComposeUI 95.99% <100.00%> (+0.54%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...seNode/RenderItem/RenderableTransition+Slide.swift 100.00% <100.00%> (ø)
...rces/ComposeUI/ComposeView/ComposeView+Debug.swift 85.71% <ø> (ø)
...ces/ComposeUI/ComposeView/ComposeView+ZOrder.swift 100.00% <100.00%> (ø)
...UI/Sources/ComposeUI/ComposeView/ComposeView.swift 98.28% <100.00%> (ø)
...seUI/CrossPlatform/ScrollView/BaseScrollView.swift 96.55% <ø> (+10.63%) ⬆️
...rossPlatform/ScrollView/ScrollView+Scrolling.swift 100.00% <100.00%> (ø)
...omposeUI/CrossPlatform/ScrollView/ScrollView.swift 79.87% <100.00%> (+21.25%) ⬆️

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e15f3f5608

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

On UIKit, `setBounds(_:)` assigned `bounds`, which resizes the view around
its center, while setting `frame.size` keeps its origin, so the note's
replacement changed where a resized view sits. It now says to assign
`bounds` on iOS, tvOS and visionOS, to set `frame.size`, then
`contentOffset` on macOS, and that setting `frame.size`, then
`contentOffset` resizes from the view's origin on every platform.
@honghaoz
honghaoz enabled auto-merge (squash) October 1, 2026 04:08
@honghaoz
honghaoz merged commit d74ba9c into master Oct 1, 2026
6 checks passed
@honghaoz
honghaoz deleted the scroll/uikit-named-adapter branch October 1, 2026 04:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AppKit: contentView() resolves to NSScrollView.contentView (the clip view) in optional or inferred contexts

1 participant