Skip to content

Remove LayoutViewFactory + refactor FeatureView to make it compose-first (first commit)#2

Open
iantybo wants to merge 1 commit into
mainfrom
feature_view_refactor_first_commit
Open

Remove LayoutViewFactory + refactor FeatureView to make it compose-first (first commit)#2
iantybo wants to merge 1 commit into
mainfrom
feature_view_refactor_first_commit

Conversation

@iantybo

@iantybo iantybo commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Mirror of the first commit of instacart/formula#511 for CodeRabbit review.

Head is 4fc15c1 (the first commit of PR #511) against the same base commit (f8f1aa3) the original ran against.

Original PR: instacart/formula#511

Risk Score: High - Possible Team Escalation Required

Summary

This PR represents a major architectural refactoring that eliminates Android View-based abstractions and establishes Jetpack Compose as the primary rendering mechanism. The changes remove two key abstraction layers (LayoutViewFactory and ViewInstance), refactor the core FeatureView component to be Compose-centric, consolidate ComposeViewFactory from a separate module into the main library, and update all affected samples and tests accordingly.

Key Changes

Core Architectural Refactoring:

  • FeatureView signature completely changed from FeatureView<RenderModel>(val view: View, val setOutput: (RenderModel) -> Unit) to FeatureView<RenderModel>(val content: @composable (RenderModel) -> Unit, val initialModel: RenderModel? = null), making Compose rendering the primary contract
  • Removed LayoutViewFactory and ViewInstance entirely, eliminating the XML layout inflation abstraction
  • FormulaFragment now uses Jetpack Compose interop directly: creates ComposeView, manages state via MutableState, and composes via featureView.content(state)

Module Consolidation:

  • Moved ComposeViewFactory from separate formula-android-compose module into formula-android
  • Removed entire formula-android-compose module and its build configuration
  • Removed ViewFactory.fromLayout() static factory and simplified ViewFactory.Params to accept only context (removed LayoutInflater and ViewGroup)

Sample Updates:

  • TodoApp sample: Migrated from XML layout files and RenderView-based Android views to Jetpack Compose, replacing TaskListRenderView with TaskListScreen composable
  • Navigation sample and Stopwatch sample: Updated imports to use ComposeViewFactory from the relocated module
  • Updated documentation examples to reflect Compose-first patterns

Test Infrastructure:

  • TestViewFactory refactored to work with Compose FeatureView instead of LayoutViewFactory
  • Updated FormulaFragmentTest to verify render exceptions are no longer routed to environment#onScreenError
  • Removed obsolete View-based tests (ViewFactoryTest, ViewInstanceTest)

Impact

This is a breaking change that eliminates multiple public APIs and requires consumers to:

  • Replace LayoutViewFactory implementations with ComposeViewFactory
  • Update ViewFactory.Params usage to only pass context
  • Refactor any ViewInstance-based patterns to use Compose-based rendering

The refactoring is systematic and well-coordinated, with all related modules, samples, and tests updated in a single PR. However, the magnitude of breaking changes requires coordination with dependent projects and careful migration planning.

@iantybo

iantybo commented Jun 15, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 6d17af70-2d89-432b-96a1-8f0e3111370a

📥 Commits

Reviewing files that changed from the base of the PR and between f8f1aa3 and 4fc15c1.

📒 Files selected for processing (35)
  • docs/Formula-Android.md
  • formula-android-compose/.gitignore
  • formula-android-compose/build.gradle.kts
  • formula-android-compose/gradle.properties
  • formula-android-compose/src/main/AndroidManifest.xml
  • formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt
  • formula-android-tests/src/test/java/com/instacart/formula/FormulaFragmentTest.kt
  • formula-android-tests/src/test/java/com/instacart/formula/TestFeatureFactory.kt
  • formula-android/build.gradle.kts
  • formula-android/src/main/java/com/instacart/formula/android/ComposeViewFactory.kt
  • formula-android/src/main/java/com/instacart/formula/android/FeatureFactory.kt
  • formula-android/src/main/java/com/instacart/formula/android/FeatureView.kt
  • formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt
  • formula-android/src/main/java/com/instacart/formula/android/LayoutViewFactory.kt
  • formula-android/src/main/java/com/instacart/formula/android/ViewFactory.kt
  • formula-android/src/main/java/com/instacart/formula/android/ViewInstance.kt
  • formula-android/src/main/java/com/instacart/formula/android/views/DelegateLayoutViewFactory.kt
  • formula-android/src/main/java/com/instacart/formula/android/views/InflatedViewInstance.kt
  • formula-android/src/test/java/com/instacart/formula/android/ViewFactoryTest.kt
  • formula-android/src/test/java/com/instacart/formula/android/views/ViewInstanceTest.kt
  • samples/navigation-fragments/build.gradle.kts
  • samples/navigation-fragments/src/main/java/com/instacart/formula/navigation/CounterFragmentViewFactory.kt
  • samples/stopwatch-compose/build.gradle.kts
  • samples/stopwatch-compose/src/main/java/com/instacart/formula/compose/stopwatch/StopwatchFeatureFactory.kt
  • samples/todoapp/build.gradle.kts
  • samples/todoapp/src/main/java/com/examples/todoapp/tasks/TaskListFeatureFactory.kt
  • samples/todoapp/src/main/java/com/examples/todoapp/tasks/TaskListRenderView.kt
  • samples/todoapp/src/main/java/com/examples/todoapp/tasks/TaskListScreen.kt
  • samples/todoapp/src/main/res/layout/task_item.xml
  • samples/todoapp/src/main/res/layout/task_list.xml
  • samples/todoapp/src/main/res/menu/tasks_list_menu.xml
  • settings.gradle.kts
  • test-utils/android/build.gradle.kts
  • test-utils/android/src/main/java/com/instacart/testutils/android/TestViewFactory.kt
  • test-utils/android/src/main/res/layout/test_fragment_layout.xml
💤 Files with no reviewable changes (19)
  • samples/navigation-fragments/build.gradle.kts
  • samples/todoapp/src/main/res/menu/tasks_list_menu.xml
  • formula-android-compose/.gitignore
  • samples/todoapp/src/main/java/com/examples/todoapp/tasks/TaskListRenderView.kt
  • formula-android/src/main/java/com/instacart/formula/android/views/InflatedViewInstance.kt
  • formula-android/src/test/java/com/instacart/formula/android/ViewFactoryTest.kt
  • samples/todoapp/src/main/res/layout/task_list.xml
  • formula-android/src/test/java/com/instacart/formula/android/views/ViewInstanceTest.kt
  • formula-android-compose/gradle.properties
  • test-utils/android/src/main/res/layout/test_fragment_layout.xml
  • formula-android-compose/src/main/AndroidManifest.xml
  • samples/todoapp/src/main/res/layout/task_item.xml
  • formula-android/src/main/java/com/instacart/formula/android/ViewInstance.kt
  • formula-android-compose/build.gradle.kts
  • formula-android/src/main/java/com/instacart/formula/android/LayoutViewFactory.kt
  • samples/stopwatch-compose/build.gradle.kts
  • formula-android-compose/src/main/java/com/instacart/formula/android/compose/ComposeViewFactory.kt
  • formula-android/src/main/java/com/instacart/formula/android/views/DelegateLayoutViewFactory.kt
  • settings.gradle.kts

📝 Walkthrough

Walkthrough

ComposeViewFactory is moved from the separate formula-android-compose module into formula-android. FeatureView is refactored to hold a Compose content lambda and optional initialModel instead of an Android View and setOutput callback. FormulaFragment is rewritten to render via ComposeView. All XML view infrastructure (LayoutViewFactory, ViewInstance, DelegateLayoutViewFactory) is removed. The todoapp sample and test utilities are migrated to the new Compose API, and formula-android-compose is deleted as a module.

Changes

Compose Rendering Migration

Layer / File(s) Summary
FeatureView and ViewFactory contract refactor
formula-android/src/main/java/com/instacart/formula/android/FeatureView.kt, formula-android/src/main/java/com/instacart/formula/android/ViewFactory.kt, formula-android/src/main/java/com/instacart/formula/android/ComposeViewFactory.kt, formula-android/src/main/java/com/instacart/formula/android/FeatureFactory.kt
FeatureView constructor changes from (view: View, setOutput) to (content: @composable (RenderModel) -> Unit, initialModel?). ViewFactory.Params is narrowed to context only and fromLayout is removed. New ComposeViewFactory abstract class is added to formula-android with create, initialModel(), and abstract Content(model).
FormulaFragment Compose rendering pipeline
formula-android/build.gradle.kts, formula-android/src/main/java/com/instacart/formula/android/FormulaFragment.kt
onCreateView creates a ComposeView, holds outputState as MutableState, and calls featureView.content(state) with DisposeOnViewTreeLifecycleDestroyed. tryToSetState updates state.value via routeDelegate.setOutput. onDestroyView clears outputState. Build script enables Compose and exposes compose.ui as an API dependency.
Remove formula-android-compose module
settings.gradle.kts, formula-android-compose/build.gradle.kts, formula-android-compose/gradle.properties, formula-android-compose/src/main/AndroidManifest.xml, formula-android-compose/src/main/java/.../compose/ComposeViewFactory.kt, formula-android-compose/.gitignore
The entire formula-android-compose Gradle module and its ComposeViewFactory implementation are deleted. settings.gradle.kts removes the :formula-android-compose include.
Remove XML view infrastructure
formula-android/src/main/java/com/instacart/formula/android/LayoutViewFactory.kt, .../ViewInstance.kt, .../views/DelegateLayoutViewFactory.kt, .../views/InflatedViewInstance.kt, formula-android/src/test/.../ViewFactoryTest.kt, .../ViewInstanceTest.kt, test-utils/android/build.gradle.kts, test-utils/android/src/main/java/.../TestViewFactory.kt, test-utils/android/src/main/res/layout/test_fragment_layout.xml
LayoutViewFactory, ViewInstance, DelegateLayoutViewFactory, InflatedViewInstance, and their tests are deleted. TestViewFactory is rewritten as a plain ViewFactory returning a FeatureView with a (RenderModel) -> Unit callback. Compose is enabled in the test-utils build script.
FormulaFragment test updates
formula-android-tests/src/test/java/com/instacart/formula/FormulaFragmentTest.kt, .../TestFeatureFactory.kt
The onScreenError routing test is replaced with a test asserting render exceptions are no longer routed there. sendStateUpdate idles the Robolectric main looper only on the main thread. TestFeatureFactory updates its lambda to match the new single-parameter TestViewFactory callback.
todoapp sample Compose migration
samples/todoapp/build.gradle.kts, .../tasks/TaskListFeatureFactory.kt, .../tasks/TaskListScreen.kt, .../tasks/TaskListRenderView.kt, samples/todoapp/src/main/res/layout/task_list.xml, .../layout/task_item.xml, .../menu/tasks_list_menu.xml
TaskListViewFactory switches from LayoutViewFactory to ComposeViewFactory. TaskListScreen.kt is added with Scaffold, LazyColumn, filter DropdownMenu, and checkbox task rows. TaskListRenderView and all XML layouts are deleted. Compose UI artifacts are added to the build script.
Sample import fixes and docs
samples/navigation-fragments/build.gradle.kts, .../CounterFragmentViewFactory.kt, samples/stopwatch-compose/build.gradle.kts, .../StopwatchFeatureFactory.kt, docs/Formula-Android.md
CounterFragmentViewFactory and StopwatchFeatureFactory update their ComposeViewFactory import to com.instacart.formula.android. Both sample build scripts drop the formula-android-compose dependency. Docs and KDoc examples are updated to show ComposeViewFactory usage.
🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.52% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary objective: removing LayoutViewFactory and refactoring FeatureView to prioritize Compose-based rendering, which matches the comprehensive changes throughout the codebase.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Performance And Algorithmic Complexity Review ✅ Passed No material performance regressions found. Refactoring eliminates XML inflation overhead, simplifies object creation, and leverages Compose recomposition skipping via structural equality.
Gdpr And Pii Data Protection Compliance ✅ Passed PR contains only UI framework refactoring (XML to Compose); no PII fields, APIs, storage, or credential handling present in any code changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature_view_refactor_first_commit

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

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.

2 participants