Repository navigation
Conversation
Adds a carousel backed by the native AirshipEmbeddedCarousel on iOS (AirshipScenes) and Android (urbanairship-automation-compose). Every pending instance for an embedded ID is shown as a swipeable page with the default chrome. The current page can be set from JS via currentPage, and page, page count and availability are reported through events. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Code ReviewTook a look through the Android/iOS/JS changes — the carousel state handling (page clamping, availability reporting, event parity between platforms) looks solid and follows the existing One thing worth a second look before merging:
Is raising the minimum Kotlin version here intentional and documented somewhere (CHANGELOG/README), or should the Compose dependency be scoped so it only applies when the carousel is actually used? Also a minor nit: the 🤖 Generated with Claude Code |
|
Sorry I'm late to this, but I think that adding Compose may cause issues because it'll require us to apply the Compose compiler to the Gradle build for all frameworks, and I'm not sure that they'll all work well with it. I don't think it's a bad idea to try (maybe it will work ok and I'm just being overly cautious?), but we may need to go back to the Android repo to add a carousel built with XML Views if Compose can't be brought into all the frameworks cleanly (Cordova and Unity are the ones that I'm particularly unsure about). |
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…/height Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
|
||
| // Falls back to the screen size until the first layout pass, matching the | ||
| // embedded view's sizing rules. | ||
| private var parentSize: CGSize { |
There was a problem hiding this comment.
parentWidth/parentHeight are optional so the SDK can resolve percent placements against the SwiftUI proposal; parentSize makes them never nil, and because the guard requires both axes, a zero height replaces the width too. The wrapper is built from initWithFrame:CGRectZero, so the first layout pass sizes percent-based content against the whole screen. main has since replaced this fallback in ReactAirshipEmbeddedView.ViewModel with per-axis nil — a rebase would bring the comment above back in line too.
| - (instancetype) init { | ||
| self = [self initWithFrame:CGRectZero]; | ||
| if (self) { | ||
| self.wrapper = [[RNAirshipEmbeddedCarouselBridgeClass() alloc] initWithFrame:self.bounds]; |
There was a problem hiding this comment.
RNAirshipEmbeddedView sets clipsToBounds = YES right here for the same hosted scene content. Without it a carousel taller than the box Yoga assigned paints over whatever sits below — in the example screen, the Instance Selection card under the 300pt container.
|
|
||
| self.viewModel.size = frame.size | ||
|
|
||
| self.cancellable = Publishers.CombineLatest( |
There was a problem hiding this comment.
No removeDuplicates, and the SDK reassigns currentPage on every pending-set change, often to the value it already held, so identical state crosses the bridge repeatedly. onStateChanged is the one callback the JS side doesn't filter against lastState, so it fires each time with a fresh object. The Android path dedupes the same stream.
|
|
||
| @objc(setPage:) | ||
| public func setPage(_ page: Int) { | ||
| guard page != lastRequestedPage else { return } |
There was a problem hiding this comment.
lastRequestedPage is never cleared, so once a page index has been requested it can't be requested again for the life of the view — and Fabric pools these, so a recycled wrapper carries the previous placement's value. Is the guard there for anything beyond redundant updateProps calls?
What do these changes do?
Adds an
AirshipEmbeddedCarouselcomponent backed by the native carousel on both platforms (AirshipEmbeddedCarouselinAirshipSceneson iOS, and inurbanairship-automation-composeon Android). It shows every pending instance for an embedded ID as swipeable pages with the default chrome (indicator, arrows).Props:
embeddedId,selection,style,currentPage(settable), and the callbacksonPageChanged,onPageCountChanged,onAvailabilityChanged,onStateChanged. Sizing follows the same rules asAirshipEmbeddedView.package.json.ComposeView-based view and manager. Adds the Compose compiler plugin and the Compose BOM (runtime,ui,foundation) toandroid/build.gradle. The carousel artifact (urbanairship-automation-compose) now comes fromairship-framework-proxy, bumped to 16.0.2 ingradle.properties.Why are these changes necessary?
React Native only exposed
AirshipEmbeddedView, which shows one instance at a time, so apps could not display a multi-instance embedded placement the way a native app can. Custom chrome is supplied as native view builders and does not bridge, so the default chrome is used.Anything else a reviewer should know?
urbanairship-automation-composeas anapidependency and updates the Android SDK to 21.0.3, so Android resolves the carousel artifact transitively. Until 16.0.2 is on Maven Central, Android builds of this branch will not resolveScreen_recording_20261008_175034.webm
Screen.Recording.iPhone.17.Pro.08-10-2026.at.17.51.16.mp4