* Fix memory leaks in UIView layout components Use WeakReference to break retain cycles in UIViewBox and UIViewFlexContainer. In Kotlin/Native, lambdas and anonymous objects create strong references by default. The SizeListener callbacks were capturing their parent containers strongly, creating retain cycles that prevented deallocation: - UIViewBox.View → children → insert lambda → SizeListener → View - UIViewFlexContainer → NodeSizeListener → enclosing container This caused UI components to remain in memory after they should have been deallocated (e.g., after logout), along with their associated graphics buffers (VM: Memory Tag objects). The fix uses WeakReference for the captured parent references, allowing proper cleanup when the parent containers are no longer needed. * Fix memory leak in RedwoodUIView sizeListener Use WeakReference to break retain cycle in RedwoodUIView's sizeListener. The sizeListener was capturing valueRootView strongly, creating a retain cycle: RedwoodUIView → valueRootView → sizeListener → valueRootView This prevented RedwoodUIView instances from being deallocated after they were removed from the view hierarchy. The fix uses WeakReference for the captured valueRootView reference, allowing proper cleanup when the view is no longer needed. * Fix memory leak: Clear widgetSystem in HostProtocolAdapter.close() The HostProtocolAdapter was retaining a reference to the WidgetSystem even after close() was called. This created a leak chain: HostProtocolAdapter (Kotlin) → WidgetSystem (Kotlin) → RealTreehouseWidgetFactory (Swift) → ContentListener (Swift) This prevented Swift objects from being deallocated even though they had no strong references visible in the memory graph, because they were being retained by Kotlin/Native's reference counting. The fix makes widgetSystem nullable and clears it in close(), breaking the Kotlin→Swift reference chain and allowing proper cleanup. * Add CHANGELOG items for the leaks
39 KiB
Change Log
Unreleased
Fixed:
- Fix memory leaks in UIView layout components (
UIViewBox,UIViewFlexContainer,RedwoodUIView) - Fix memory leak in
HostProtocolAdapter
These leaks affected some Treehouse-based screens and prevented UI components and their associated memory from being released after dismissal.
0.19.0
This will be the final release of Redwood for the foreseeable future. Development will be discontinued at this time.
New:
- Focus API. Redwood has a new
FocusRequesterto focus a widget.
Changed:
- Android libraries now have a minimum SDK level of 23.
- iOS x64 targets are no longer supported.
- Use AndroidX collection and Compose runtime artifacts, which are now fully multiplatform. A dependency constraint to the JetBrains Compose runtime version 1.9.0 has been added, which is the first version that is empty and itself now points to AndroidX.
Fixed:
- Don't measure the height of
RedwoodUIViewas zero when measured withsizeThatFits()orintrinsicContentSize(). We had been usingUIStackViewwhich doesn't support these functions. - Correctly signal
RedwoodUIViewsize changes of to callers who measure it withintrinsicContentSize(). - The Redwood Gradle plugin is now compatible with Gradle 9.1.
- Don't incorrectly size items to 0 when using the
Flex()modifier on aWrapcontainer.
0.18.0 - 2025-08-01
New:
- Schema
@Widgets can now setinternalComposable = trueto have their@Composablefunctions generated as internal. This will require that you define a public version in the main sources of the module which generates the functions. This can be used to hide old widgets that should no longer be used, create more complex widget protocols away from callers, and to conditionally split implementation between two bindings, for example. - UI changes which come from Treehouse are now converted to their final value on a background thread. Previously JSON deserialization happened on the background thread to an intermediate model, but mapping that model to the final value still occurred on the main thread.
Changed:
- Schema dependencies can now be a graph (i.e., dependencies can have their own dependencies), but the entire transitive set needs to be redeclared on the root schema (for the protocol to work properly).
- Compose UI widget type has been changed from
@Composable () -> Unitto@Composable (Modifier) -> Unitto support unscoped modifiers. - JVM and Android artifacts now target Java 11 bytecode, as the upstream Compose dependencies now all target Java 11.
- The host protocol type has been renamed from
ProtocolFactorytoHostProtocol. An instance ofHostProtocolis now required when constructing aTreehouseAppFactory. - Enforce that event properties declared in your schema always return
Unit. - In-development snapshots are now published to the Central Portal Snapshots repository at https://central.sonatype.com/repository/maven-snapshots/.
Fixed:
- Don't double insets on insets-aware
UIViews. Previously we offered the same insets via two mechanisms, which could result in double insets. - Don't conflate
CrossAxisAlignment.StretchwithContraint.Fill. We had a bug whereCrossAxisAlignment.Stretchwould cause children to fill their parent container. - Honor the inbound max width for
RowandColumnlayouts usingConstraint.Wrapon iOS. This is necessary for child components that can wrap, like text. - Using "stretch" cross-axis alignment on a lazy list now works correctly in Compose UI.
Upgraded:
- Kotlin 2.2.0
- Zipline 1.21.0
0.17.0 - 2025-01-30
Breaking:
- Treehouse hosts running Redwood 0.11.0 or older are not longer actively supported. They will continue to work, but they will experience indefinite memory leaks of native widgets.
- Old, deprecated overloads of
ZiplineTreehouseUi.starthave been removed. The new overloads have been available since Redwood 0.8.0 for over a year.
New:
UIConfiguration.viewInsetstracks the safe area of the specificRedwoodViewbeing targeted. This is currently implemented for views on Android and UIViews on iOS.ConsumeInsets {}composable consumes insets. Most applications should call this in their root composable function.- Add
TestRedwoodComposition.setContentAndSnapshotfunction which is a fused version ofsetContentandawaitSnapshot, except that it guarantees the returned snapshot is the result of the initial composition of the content without any additional frames sent.
Fixed:
- Fix inconsistency in margin application between
ComposeUiBoxandViewBox. - Add support for the Height modifier in
ComposeUiBox. - Add support for the Width modifier in
ComposeUiBox. - Call
DisposableEffectwhen a screen is unbound. We were only calling these when the effect was removed from the composition. - Support
movableContentOfin Treehouse (and generally in the Redwood protocol). Note: this requires the host be running version 0.17.0 or newer. - Fix case where
ColumnandRowwould not update their intrinsic size on iOS if they are not a child of anotherColumnorRow.
0.16.0 - 2024-11-19
New:
- Redwood publishes what's happening in bound content through the new
Content.Statetype. - Accept a
ZiplineHttpClientinTreehouseAppFactoryon Android.
Changed:
- Drop support for non-incremental layouts in
RowandColumn. - Support for
@Defaultannotation has now been removed, as detailed in the 0.15.0 release.
Fixed:
- Fix a layout bug where children of fixed-with
Rowcontainers were assigned the wrong width. - Fix inconsistencies between iOS and Android for
ColumnandRowlayouts. - Fix a layout bug where
RowandColumnlayouts reported the wrong dimensions if their subviews could wrap. - Correctly update the layout when a Box's child's modifiers are removed.
- Fix a layout bug where children of
Boxcontainers were not measured properly. - Fix a bug where
LazyColumndidn't honor child widget resizes.
Breaking:
- Replace
CodeListenerwith a newDynamicContentWidgetFactoryAPI. Now loading and crashed views work like all other child widgets.
0.15.0 - 2024-09-30
New:
- Default expressions can now be used directly in the schema rather than using the
@Defaultannotation. The annotation has been deprecated, and will be removed in the next release. EventListener.Factory.close()is called byTreehouseApp.close()to release any resources held by the factory.- Lambda parameter names defined in the schema are now propagated to the generated composable and widget interface.
ResizableWidgetis an interface thatUIViewwidgets must use if their intrinsic sizes may change dynamically. It notifies any enclosing parent views to trigger a new layout.
Changed:
- Removed Wasm JS target. We are not ready to support it yet.
Fixed:
- Breaking the last remaining retain cycle in
UIViewLazyList. - Don't leak the
DisplayLinkwhen aTreehouseAppis stopped on iOS. - Correctly handle dynamic size changes for child widgets of
Box,Column, andRow. - Don't clip elements of
ColumnandRowlayouts whose unbounded size exceeds the container size. - Correctly implement margins for
Boxon iOS. - Correctly handle dynamic updates to modifiers on
ColumnandRow.
0.14.0 - 2024-08-29
New:
- Source-based schema parser is now the default. The
useFirGradle property has been removed. TreehouseAppFactoryaccepts aLeakDetectorwhich can be used to notify you of reference leaks for native UI nodes, Zipline instances, Redwood's own internal wrappers, and more.- Introduce a
LoadingStrategyinterface to manageLazyListpreloading. - Optimize encoding modifiers in Kotlin/JS.
Changed:
- In Treehouse, events from the UI are now serialized on a background thread. This means that there is both a delay and a thread change between when a UI binding sends an event and when that object is converted to JSON. All arguments to events must not be mutable and support property reads on any thread. Best practice is for all event arguments to be completely immutable.
ProtocolFactoryinterface is now sealed as arbitrary subtypes were never supported. Only schema-generated subtypes should be used.UIViewLazyListdoesn't crash with aNullPointerExceptionif cells are added, removed, and re-added without being reused.- Change
UiConfiguration.viewportSizeto be nullable. A nullviewportSizeindicates the viewport's size has not been resolved yet.
Fixed:
- Breaking
content: UIViewretain cycle inUIViewLazyList'sLazyListContainerCell. - Update
ProtocolNodewidget IDs when recycling widgets. This was causing pooled nodes to be leaked.
Breaking:
- The
TreehouseApp.specproperty is removed. Most callers should be able to useTreehouseApp.nameinstead. This is necessary to avoid a retain cycle.
Upgraded:
- Kotlin 2.0.20
- Zipline 1.17.0
0.13.0 - 2024-07-25
New:
- Wasm JS added as a target for common Redwood modules. There is no Treehouse support today.
- Add
onScrollproperty toRowandColumn. This property is invoked whenoverflow = Overflow.Scrolland the container is scrolled. - Add
Pxclass to represent a raw pixel value in the host's coordinate system. - New source-based schema parser can be enabled with
redwood { useFir = true }in your schema module. Please report and failures to the issue tracker. This parser will become the default in 0.14.0.
Changed:
- The
TreehouseApptype is now an abstract class. This should make it easier to write unit tests for code that integrates Treehouse. - The
TreehouseApp.Spec.bindServices()function is now suspending. - The
TreehouseAppFactoryfunction now accepts a ZiplineLoaderEventListenerparameter.
Fixed:
- Using a
data objectfor a widget of modifier no longer causes schema parsing to crash. - Ensuring
LazyList'sitemsBeforeanditemsAfterproperties are always within[0, itemCount], to preventIndexOutOfBoundsExceptioncrashes. - Don't crash in
LazyListwhen a scroll and content change occur in the same update. - Updating a flex container's margin now works correctly for Yoga-based layouts.
Breaking:
- The
TreehouseApp.Factory.dispatchersproperty is removed, and callers should migrate toTreehouseApp.dispatchers. With this update eachTreehouseApphas its own private thread so a shareddispatchersproperty no longer fits our implementation. -TreehouseApp.Spec.bindServices()now accepts aTreehouseAppparameter.
Upgraded:
- Zipline 1.16.0
0.12.0 - 2024-06-18
New:
- Upgrade to Kotlin 2.0!
- Added a basic DOM-based
LazyListimplementation. -TreehouseApp.close()stops the app and prevents it from being started again later. - Added
UiConfiguration.layoutDirectionto support reading the host's layout direction. - New
redwood-bomartifact can be used to ensure all Redwood artifacts use the same version. See Gradle's documentation on how to use the BOM in your build.
Changed:
- The
app.cash.redwoodGradle plugin has been removed. This plugin did two things: apply the Compose compiler and add a dependency on theredwood-composeartifact. The Compose compiler can now be added by applying theorg.jetbrains.kotlin.plugin.composeGradle plugin. Dependencies on Redwood artifacts can be added manually. - Removed deprecated
typealiases for generated-WidgetFactoriestype which was renamed to-WidgetSystemin 0.10.0. - Removed deprecated
Modifier.flexextension function which is now supported natively byRowandColumnsince 0.8.0. - Removed deprecated
TreehouseWidgetViewandTreehouseUIKitViewtype aliases forTreehouseLayoutandTreehouseUIViewwhich were renamed in 0.7.0. - Removed deprecated
TreehouseAppFactoryfunctions with the oldFileSystemandPathorder which were changed in 0.11.0. - Rename the two types named
ProtocolBridgetoProtocolHostandProtocolGuest.
Fixed:
- Fix memory leaks caused by reference cycles on iOS. We got into trouble mixing garbage-collected Kotlin objects with reference-counted Swift objects.
Breaking:
-TreehouseApp.zipline is now a StateFlow<Zipline?> instead of a Zipline?.
-CodeListener.onCodeDetached() replaces onUncaughtException(). The new function is called
whenever code stops driving a view for any reason. The new function accepts a Throwable? that is
non-null if it's detached due to exception.
-Content.awaitContent() now accepts an optional Int parameter for the number of updates to
observe before the function returns.
- MacOS targets have been removed from all modules.
Upgraded:
- Kotlin 2.0.0
- Zipline 1.13.0
- kotlinx.serialization 1.7.0
Gradle plugin removed
This version of Redwood removes the custom Gradle plugin in favor of the official JetBrains Compose compiler plugin which ships as part of Kotlin itself.
Each module in which you had previously applied the app.cash.redwood plugin should be changed to apply org.jetbrains.kotlin.plugin.compose instead.
The Redwood dependencies will no longer be added as a result of the plugin change, and so any module which references Redwoods APIs should add those dependencies explicitly.
For posterity, the Kotlin version compatibility table and compiler version customization for our old Redwood Gradle plugin will be archived here:
Redwood 0.12.0 Gradle plugin Kotlin compatibility table
Since Kotlin compiler plugins are an unstable API, certain versions of Redwood only work with certain versions of Kotlin.
| Kotlin | Redwood |
|---|---|
| 1.9.24 | 0.11.0 |
| 1.9.23 | 0.10.0 |
| 1.9.22 | 0.8.0 - 0.9.0 |
| 1.9.10 | 0.7.0 |
| 1.9.0 | 0.6.0 |
| 1.8.22 | 0.5.0 |
| 1.8.20 | 0.3.0 - 0.4.0 |
| 1.7.20 | 0.1.0 - 0.2.1 |
Redwood 0.12.0 Gradle plugin Compose compiler customization instructions
Each version of Redwood ships with a specific JetBrains Compose compiler version which works with a single version of Kotlin (see version table above). Newer versions of the Compose compiler or alternate Compose compilers can be specified using the Gradle extension.
To use a new version of the JetBrains Compose compiler version:
redwood {
kotlinCompilerPlugin.set("1.4.8")
}
To use an alternate Compose compiler dependency:
redwood {
kotlinCompilerPlugin.set("com.example:custom-compose-compiler:1.0.0")
}
0.11.0 - 2024-05-15
New:
- Added
toDebugStringmethod forWidgetValueandList<WidgetValue>which returns a formatted string of a widget's children and properties, useful for test debugging.
Changed:
- Removed generated
typealiases for package names which changed in 0.10.0. - In
UIViewLazyList'sUITableView, adding special-case handling for programmatic scroll-to-top calls. - APIs accepting a
FileSystemandPathnow have theFileSystemcoming before thePathin the parameter list. Compatibility functions are retained for this version, but will be removed in the next version. - Change
LazyListStateto be scroll-aware, reducing the size of the preload window while actively scrolling, and optimizing the preload window once the scroll has completed.
Fixed:
- Work around a problem with our memory-leak fix where our old LazyList code would crash when its placeholders were unexpectedly removed.
- Avoid calling into the internal Zipline instance from the UI thread on startup. This would manifest as weird native crashes due to multiple threads mutating shared memory.
- In
UIViewLazyList, fixUInttoUIColorconversion math used forpullRefreshContentColor. - In
YogaUIView'ssetScrollEnabledmethod, only callsetNeedsLayoutif thescrollEnabledvalue is actually changing. - In
YogaUIView'slayoutNodesmethod, return early for nestedYogaUIViews to prevent redundant frame calculations.
Upgraded:
- Zipline 1.10.1.
This version works with Kotlin 1.9.24 by default.
0.10.0 - 2024-04-05
New:
- Compose UI implementation for
Box. - Layout modifier support for HTML DOM layouts.
- Unscoped modifiers provide a global hook for side-effecting behavior on native views. For example, create a background color modifier which changes the platform-native UI node through a factory function.
Widget.Childreninterface now exposeswidgets: List<Widget<W>>property. Most subtypes were already exposing this individually.
Changed:
- Disable klib signature clash checks for JS compilations. These occasionally occur as a result of Compose compiler behavior, and are safe to disable (the first-party JetBrains Compose Gradle plugin also disables them).
onModifierChangedcallback inWidget.Childrennow receives the index and theWidgetinstance affected by the change.- The package of 'redwood-protocol-host' changed to
app.cash.redwood.protocol.host. This should not affect end-users as its types are mostly for internal use. - The entire
redwood-yogaartifact's public API has been annotated with an opt-in annotation indicating that it's only for Redwood internal use and is not stable. - Revert: Don't block touch events to non-subviews below a
Row,Column, orBoxin the iOSUIViewimplementation. This matches the behavior of the Android View and Compose UI implementations. - The generated "widget factories" type (e.g.,
MySchemaWidgetFactories) is now called a "widget system" (e.g.,MySchemaWidgetSystem). Sometimes it was also referred to as a "provider" in parameter names. A@Deprecated typealiasis generated for now, but will be removed in the future. - The package names of some generated code has changed. Deprecated
typealiases are generated in the old locations for public types and functions, but those will be removed in the next release.- Testing code is now under
your.package.testing. - Protocol guest code is now under
your.package.protocol.guest. - Protocol host code is now under
your.package.protocol.host.
- Testing code is now under
- The 'app.cash.redwood.generator.compose.protocol' and 'app.cash.redwood.generator.widget.protocol' Gradle plugins are now deprecated and will be removed in the next release. Use 'app.cash.redwood.generator.protocol.guest' and 'app.cash.redwood.generator.protocol.host', respectively.
- The 'redwood-tooling-codegen' CLI flags for protocol codegen have changed from
--compose-protocoland--widget-protocolto--protocol-guestand--protocol-host, respectively. - Entrypoints to the protocol on the host-side and guest-side now require supplying the version of Redwood in use on the other side in order to ensure compatibility and work around any bugs in older versions. This uses a new
RedwoodVersiontype, and will be automatically wired if using our Treehouse artifacts.
Fixed:
- Fix failure to release JS resources when calling
CoroutineScopeis being cancelled - JVM targets now correctly link against Java 8 APIs. Previously they produced Java 8 bytecode, but linked against the compile JDK's APIs (21). This allowed linking against newer APIs that might not exist on older runtimes, which is no longer possible. Android targets which also produce Java 8 bytecode were not affected.
- Fix the
Viewimplementation ofBoxto wrap its width and height by default. This matches the behavior of theUIViewimplementation and all other layout widgets. - Fix the
UIViewimplementation ofBoxnot updating when some of its parameters are changed. - Fix
Modifier.sizenot being applied to children inside aBox. - Fix
Marginnot being applied to theUIViewimplementation ofBox. - The
Viewimplementation ofBoxnow applies start/end margins correctly in RTL, and does not crash if set before the native view was attached. - Fix the backgroundColor for
UIViewLazyListto be transparent. This matches the behavior of the otherLazyListplatform implementations. - Fix
TreehouseUIViewto size itself according to the size of its subview. - In
UIViewLazyList, addingbeginUpdates/endUpdatescalls toinsertRows/deleteRows, and wrapping changes inUIView.performWithoutAnimationblocks. - Fix memory leak in 'protocol-guest' and 'protocol-host' where child nodes beneath a removed node were incorrectly retained in an internal map indefinitely. The guest protocol code has been updated to work around this memory leak when deployed to old hosts by sending individual remove operations for each node in the subtree.
- Ensure that Zipline services are not closed prematurely when disposing a Treehouse UI.
- In
UIViewLazyList, don't remove subviews from hierarchy duringprepareForReusecall
This version works with Kotlin 1.9.23 by default.
0.9.0 - 2024-02-28
Changed:
- Added
Modifierparameter toRedwoodContentwhich is applied to the rootBoxinto which content is rendered (https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-api-guidelines.md#elements-accept-and-respect-a-modifier-parameter). - The parameter order of
LazyRowandLazyColumnhave changed to reflect Compose best practices (https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-api-guidelines.md#elements-accept-and-respect-a-modifier-parameter). - The parameter order of
TreehouseContenthas changed to reflect Compose best practices (https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-api-guidelines.md#elements-accept-and-respect-a-modifier-parameter). - The render function of
ComposeWidgetChildrenhas been renamed toRenderto reflect Compose best practices (https://android.googlesource.com/platform/frameworks/support/+/androidx-main/compose/docs/compose-api-guidelines.md#naming-unit-composable-functions-as-entities). - Disable decoy generation for JS target to make compatible with JetBrains Compose 1.6. This is an ABI-breaking change, so all Compose-based libraries targeting JS will also need to have been recompiled.
Fixed:
- Don't block touch events to non-subviews below a
Row,Column, orBoxin the iOSUIViewimplementation. This matches the behavior of the Android View and Compose UI implementations.
This version works with Kotlin 1.9.22 by default.
0.8.0 - 2024-02-22
New:
flex(double)modifier for layouts which acts as a weight along the main axis.- Allow reserving widget, modifier, property, and children tags in the schema. This can be used to document old items which no longer exist and prevent their values from accidentally being reused.
- Add
dangerZone { }DSL to theredwood { }Gradle extension which allows enabling Compose reports and metrics. Currently these features break build caching as Compose forces the use of absolute paths in the Kotlin compiler arguments when in use (hence why they're marked as dangerous). BackHandlercomposable provides a callback for handling hardware back affordances (currently only on Android).- Expose
frameClockonStandardAppLifecycleto allow monitoring host frames. CodeListener.onUncaughtExceptionnotifies of any uncaught exceptions which occur in Treehouse guest code.- Preview: Add
Boxwidget which stacks children on top of each other. This is currently only implemented for Android views and iOS UIKit. - Support
rememberSaveablein plain Redwood compositions. - Programmatic scrolls on
LazyListStatecan now setanimated=truefor an animated scroll. - Add
ziplineCreated,manifestReady, andcodeLoadSkippedNotFreshevent callbacks to TreehouseEventListener.
Changed:
- The Treehouse Zipline disk cache directory is no longer within the cache directory on Android. This ensures it can't be cleared while the app is running. Zipline automatically constrains the directory to a maximum size so old entires will still be purged automatically.
- Set the Zipline thread's stack size to 8MiB on Android to match iOS.
- Use
margin-inline-startandmargin-inline-endfor the start and end margin, respectively, for the HTML DOM layout bindings. TestRedwoodCompositionnow accepts only the initialUiConfiurationand exposes aMutableStateFlowfor changing its value over time.TreehouseLayoutnow defines a default ID to allow state saving and restoration to work. Note that this will only work when a single instance is present in the hierarchy. If you have multiple, supply your own unique IDs.- Emoji Search sample applications now bundle the latest guest code at compile-time and do not require the server running to work.
- The built-in
RedwoodViewforHTMLElementnow reports density changes to theUiConfiguration. - Redwood protocol modules have been renamed to 'guest' and 'host' to match Treehouse conventions.
- Suppress deprecation warnings in generated code. This code often refers to user types which may be deprecated, and should not cause additional warnings.
TreehouseAppContent.preloadis now idempotent.LazyListon iOS has changed fromUICollectionViewtoUITableView, and changes to the backing data are now reported granularly rather than reloading everything.- Allow arbitrary serializable content within
rememberSaveableinside Treehouse. - Add a
TreehouseAppargument toCodeListener. Combined with the new uncaught exception callback, this provides an easy way to restart a Treehouse application on a crash. EventListener.Factoryinstances are now supplied as part of aTreehouseAppinstead of aTreehouseAppFactory. This more closely scopes them with the lifetime of theZiplineinstance.
Fixed:
- Ensure changes to modifiers notify their parent widget when using Treehouse.
- Explicitly mark the generated scope objects as
@Stableto prevent needless recomposition. - Dispose the old composition when the
RedwoodContentcomposable recomposes or is removed from the composition. - Ensure
UIViewChildrenindexes children usingtypedArrangedSubviewswhen removing views from aUIStackView. - Correctly parse
data objectmodifiers in the schema. - Remember the default
CodeListenerforTreehouseContentto avoid unneccessary recomposition on creation. - When calling
TreehouseUi.start, fall back to older API signature when newer one does not match. This is needed because an addiitonal parameter was added in newer versions, but older guest code may have the old signature. - Persist saved values from Treehouse without jumping back to the UI thread which allows proper restoration after a config change.
- Reset the requested widths and heights of a layout in the underlying Yoga engine when the size is invalidated. This ensures that the engine will properly measure changed content the grows and shrinks in either dimension.
This version works with Kotlin 1.9.22 by default.
0.7.0 - 2023-09-13
New:
- Expose viewport size and density in
UiConfiguration. RedwoodViewand platform-specific subtypes provide a turnkey view into which aRedwoodCompositioncan be rendered.TreehouseViewnow extendsRedwoodView.
Changed:
- Remove support for the Kotlin/JS plugin (
org.jetbrains.kotlin.js). This plugin is deprecated and projects should be migrated to Kotlin multiplatform plugin (org.jetbrains.kotlin.multiplatform). - Some
TreehouseViewsubtypes were renamed to better match platform conventions:TreehouseWidgetViewis nowTreehouseLayoutfor Android.TreehouseUIKitViewis nowTreehouseUIViewfor iOS.
UIViewChildrennow supportsUIStackViewautomatically.- Package name of types in 'lazylayout-dom' artifact is now
lazylayoutinstead of justlayout.
This version works with Kotlin 1.9.10 by default.
0.6.0 - 2023-08-10
New:
-
Support for specifying custom Compose compiler versions. This will allow you to use the latest version of Redwood with newer versions of Kotlin than it explicitly supports.
See the README for more information.
-
LazyListcan now be programmatically scrolled through itsScrollItemIndexparameter. -
Pull-to-refresh indicator color on
LazyListis now customizable throughpullRefreshContentColorparameter.
Changes:
- Many public types have been migrated away from
data classto regular classes withequals/hashCode/toString(). If you were relying on destructuring orcopy()for these types you will need to migrate to doing this manually.
Fix:
- The emoji search browser sample no longer crashes on first load.
- Lots of rendering and performance fixes for UIKit version of
LazyList- Only measure items which are visible in the active viewport.
- Remove some default item spacing imposed by the backing
UICollectionViewFlowLayout. - Share most of the internal bookkeeping logic with the Android implementations for consistency and correctness.
- Placeholders are now correctly sized along the main axis.
This version works with Kotlin 1.9.0 by default.
0.5.0 - 2023-07-05
This release marks Redwood's "beta" period which provides slightly more stability guarantees than before. All future releases up to (but NOT including) 1.0 will have protocol and service compatibility with older versions. In practice, what this means is that you can use Redwood 0.6 (and beyond) to compile and deploy Treehouse guest code which will run inside a Treehouse host from Redwood 0.5.
Redwood still reserves the right to make binary- and source-incompatible changes within the host code or within the guest code.
New:
- The relevant tags and names from your schema will now automatically be tracked in an API file and
changes will be validated to be backwards-compatible. The
redwoodApiGenerateGradle task will generate or update the file, and theredwoodApiChecktask will validate the current schema as part of thechecklifecycle task. width,height, andsizemodifiers allow precise control over widget size within Redwood layout.- Preliminary support for
rememberSaveablewithin Treehouse guest code with persistence only available on Android hosts.
Changes:
- The flexbox implementation has changed from being a Kotlin port of the Google's Java flexbox layout to using Facebook's Yoga library.
LazyListnow has arguments formarginand cross-axis alignment (verticalAlignmentforLazyRow,horizontalAlignmentforLazyColumn)- Remove the ability to use custom implementations of
LazyList. Any missing functionality from the built-in versions should be filed as a feature request. - The command-line tools (codegen, lint, schema) are now uploaded to Maven Central as standalone zip files in addition to each regular jar artifact for use with non-Gradle build systems.
Fixed:
- RTL layout direction is now supported by the Compose UI and View-based implementations of Redwood layout.
This version only works with Kotlin 1.8.22.
0.4.0 - 2023-06-09
New:
- Experimental support for refresh indicators on
LazyRowandLazyColumnviarefreshingboolean andonRefreshlambda. These are experimental because we expect refresh support to migrate to some kind of future support for widget decorators so that it can be applied to any widget. DisplayLinkClockis available for iOS and MacOS users of Redwood. (Treehouse already had a frame clock for iOS).- A
WidgetValue(orList<WidgetValue>) produced from the generated testing function'sawaitSnapshot()can now be converted to aSnapshotChangeListwhich can be serialized to JSON. That JSON can then later be deserialized and applied to aTreehouseViewto recreate a full view hierarchy from any state. This is useful for unit testing widget implementations, screenshot testing, and more. - Widget implementations can implement the
ChangeListenerinterface to receive anonEndChanges()callback which occurs after all property or event lambda changes in that batch. This can help reduce thrashing in response to changes to multiple properties or event lambdas at once. LazyRowandLazyColumnnow support aplaceholdercomposable slot which will be used with Treehouse when a new item is displayed but before its content has loaded. Additionally, the size of these widgets can now be controlled throughwidthandheightconstraints.
Changes:
-
LayoutModifierhas been renamed toModifier. -
UI primitives like
Dp,Density, andMarginhave moved from Treehouse into the Redwood runtime (in theapp.cash.redwood.uipackage). -
HostConfigurationhas moved from Treehouse into the Redwood runtime (in theapp.cash.redwood.uipackage) and is now calledUiConfiguration. -
Composables running in Treehouse now run on a background thread on iOS. Previously they were running on the main thread. Interactions with UIKit still occur on the main thread.
-
RedwoodContentfunction for hosting a Redwood composable within Compose UI has moved into a newredwood-composeuiartifact as it will soon require a Compose UI dependency. -
The generated testing function now returns the value which was returned from the testing lambda.
Before:
suspend fun ExampleTester(body: suspend TestRedwoodComposition.() -> Unit)Now:
suspend fun <R> ExampleTester(body: suspend TestRedwoodComposition.() -> R): R -
The Redwood and Treehouse frame clocks now send actual values for the frame time instead of 0.
Fixed:
- Widgets which accept nullable lambdas for events now receive an initial
nullvalue when no lambda is set. Previously anullwould only be seen after a non-nulllambda. - Reduce binary impact of each widget's composable function by eliminating a large error string generated by the Kotlin compiler for an error case whose occurrence was impossible.
- The iOS implementation of
Row,Column,Spacer, andUIViewChildrennow react to size and child view changes more accurately according to UIKit norms.
This version only works with Kotlin 1.8.20.
0.3.0 - 2023-05-15
New:
- Support for testing Composables with new test-specific code generation. Use the
'app.cash.redwood.generator.testing' plugin to generate a lambda-accepting entrypoint function
(such as
ExampleTester()). Inside the lambda you can await snapshots of the values which would be bound to the UI widgets at that time. - Redwood Layout now contains a
Spacerwhich can be used to create negative space separately from padding (which otherwise disappears when the item disappears). - The host's safe area insets are now included in
HostConfiguration. Note that these are global values which should only be applied when a view is known to be occupying the full window size. - Use the host's native frame rate to trigger recomposition inside of Treehouse. Pending snapshot changes are also required for recomposition to occur.
Changes:
- Widgets are now created, populated, and attached to the native view hierarchy in a different order
than before. Previously widget was created, attached to its parent, and then its properties were
all set followed by any language modifiers. Now, the widget is created, all of its properties and
layout modifiers are set, and then it is added to its parent. Additionally, widgets are added to
their parents in a bottom-up manner. Code like
Row { Column { Text } }will seeTextbe added toColumnbeforeColumnis added to `Row. - 'redwood-treehouse' module has been split into '-shared', '-guest', and '-host' modules to more cleanly delineate where each is used. "Host" is the native application and "guess" is code running inside the Zipline JS VM.
- Schema dependencies are not longer parsed when loading a schema. Instead, a JSON representation is loaded from the classpath which contains the parsed structure of the dependency. As a result, the module which contains the schema files must apply the 'app.cash.redwood.schema' plugin in order to create this JSON.
- Redwood Layout's
Paddingtype is now calledMargin. - Both Redwood's own API as well as code generated from your schema is now annotated with
@ObjCNameto create better-looking APIs in Objective-C (and Swift). - The
@Deprecatedannotation on a widget or its properties will now propagate into the generated Composable and widget interface. - Event types are no longer always nullable. They will now respect the nullability in the schema.
- Layout modifiers are now generated into a 'modifier' subpackage.
Fixed:
- Redwood Layout
Constraints are now correctly propagated into HTML.
This version only works with Kotlin 1.8.20.
0.2.1 - 2023-01-31
Changed:
- Do not use a
ScrollView/HorizontalScrollViewas the parent container for View-basedRowandColumndisplay when the container is not scrollable (the default). Use aFrameLayoutinstead.
Fixed:
- Actually publish the
redwood-treehouse-composeuiartifact.
This version only works with Kotlin 1.7.20.
0.2.0 - 2023-01-30
New:
redwood-layout-dommodule provides HTML implementations ofRowandColumn.- Lazy layout's schema artifacts are now published and can be used by other projects.
- Expose
concurrentDownloadsparameter forTreehouseApp.Factory. The default is 8. - Add
moduleLoadStartandmoduleLoadEndevents to Treehouse'sEventListener.
Changed:
- Compile with Android API 33.
- Counter sample now uses shared
RowandColumnlayouts rather than its own unspecified one. - JSON serialization on the Compose-side of Treehouse is now faster and emits dramatically less code than before.
- Create a dedicated
CoroutineScopefor eachTreehouseView. When a view leaves, its coroutines can now be immediately canceled without waiting for anything on the application-side. TreehouseLauncheris now calledTreehouseApp.Factory. Additionally, when youcreate()aTreehouseAppfrom a factory you must also callstart()for it to actually start.- Use platform-specific collections types in JS for the Compose-side of Treehouse. This is faster, more memory-efficient, and produces less code.
- Update to Zipline 0.9.15.
Fixed:
- Do not expose Gradle
Configurations created by our plugin. This ensures they are not candidates for downstream modules to match against when declaring a dependency on a project using the plugin. - Change when the Treehouse
FrameClockis closed to avoid crashing on updates.
This version only works with Kotlin 1.7.20.
0.1.0 - 2022-12-23
Initial release.
This version only works with Kotlin 1.7.20.