Adaptive Cards Mobile SDK - Implementation Plan
March 15, 2026 · View on GitHub
Last Updated: March 14, 2026 Target Schema: Adaptive Cards v1.6 Reference: Web/Desktop Parity Analysis
Project Overview
This document tracks the implementation progress across all phases of the Adaptive Cards Mobile SDK. For detailed property-level gap analysis against the web/desktop SDK, see WEB_DESKTOP_PARITY_ANALYSIS.md.
Phase 1: Templating Engine — 100% ✅
Status: Complete
Both iOS (ACTemplating) and Android (ac-templating) have full feature parity:
- Complete AST-based expression parsing with type-safe evaluation
- 60+ built-in functions across 5 categories (String, Math, Logic, Date, Collection)
- Full template expansion with
${...}syntax,$whenconditionals,$dataiteration - Nested data contexts with
$root,$data,$index - 40+ iOS tests, 50+ Android tests
Phase 2: Advanced Elements + Markdown + Fluent Theming — 100% ✅
Status: Complete
| Item | iOS | Android | Tests | Notes |
|---|---|---|---|---|
| 2A. Markdown Rendering | ✅ ACMarkdown | ✅ ac-markdown | ✅ | CommonMark subset: bold, italic, links, lists, inline code, headings, blockquotes |
| 2B. ListView Element | ✅ ListItemView | ✅ ListItemView | ✅ | Ordered/unordered lists with markers, nested support |
| 2C. DataGridInput | ✅ DataGridInputView | ✅ DataGridInputView | ✅ | Editable columns, rows, sorting |
| 2D. CompoundButton | ✅ CompoundButtonView | ✅ CompoundButtonView | ✅ | Title, description, icon, badge (v1.6) |
| 2E. Charts | ✅ ACCharts (4 types) | ✅ ac-charts (4 types) | ✅ | Donut, Bar, Line, Pie with accessibility |
| 2F. Fluent UI Theming | ✅ ACFluentUI | ✅ ac-fluent-ui | ✅ | FluentTheme, color tokens, HostConfig integration |
| 2G. Schema Validation | ✅ SchemaValidator | ✅ SchemaValidator | ✅ | v1.6 schema validation, structured errors |
| 2H. Model Updates | ✅ targetWidth | ✅ targetWidth | ✅ | Responsive layout (narrow/standard/wide), themed URLs |
Phase 3: Advanced Actions + Copilot Extensions + Teams — 100% ✅
Status: Complete
| Item | iOS | Android | Tests | Notes |
|---|---|---|---|---|
| 3A. Action.Popover | ✅ .sheet() | ✅ ModalBottomSheet | ✅ | Title, body elements, dismiss behavior |
| 3A. Action.RunCommands | ✅ | ✅ | ✅ | Command dispatch to host |
| 3A. Action.OpenUrlDialog | ✅ | ✅ | ✅ | URL in dialog with title |
| 3B. Menu Actions / Overflow | ✅ SwiftUI Menu | ✅ Compose DropdownMenu | ✅ | Primary/secondary mode, maxActions overflow |
| 3C. Copilot Extensions | ✅ ACCopilotExtensions | ✅ ac-copilot-extensions | ✅ | CitationView, StreamingCardView |
| 3D. Teams Integration | ✅ ACTeams | ✅ ac-teams | ✅ | TeamsCardHost, deep links, theming |
Phase 4: Sample Apps — 100% ✅
Status: Complete
| Platform | Status | Features |
|---|---|---|
| iOS (AC Visualizer) | ✅ | Card gallery, deep link navigation (adaptivecards://card/...), category browsing, card rendering with all element types |
| Android (AC Visualizer) | ✅ | Card gallery, deep link navigation, category browsing, card rendering with all element types |
Both apps support:
- Deep link navigation for automated testing
- 43+ shared test cards across all categories
- Teams Official Samples rendering
- Template card rendering with data binding
Phase 5: Production Readiness — 40% 🚧
Status: Partially complete
| Item | Status | Notes |
|---|---|---|
| 5A. Visual Regression Tests | 🚧 Scaffolding | iOS: swift-snapshot-testing scaffolding + 10 baseline tests; Android: Paparazzi scaffolding. Not fully integrated into CI |
| 5B. CI/CD Hardening | ⚠️ Partial | Workflows exist (parity-gate, ios-tests, android-tests, lint, validate-test-cards). Missing: snapshot test jobs, build matrix for multiple OS versions |
| 5C. SDK Publishing | ❌ Not started | iOS: Package.swift products defined but no version tagging/release workflow. Android: no maven-publish plugin or POM metadata |
| 5D. API Documentation | ❌ Not started | No DocC catalog (iOS) or Dokka generation (Android). Public APIs have doc comments but no generated docs |
| 5E. Performance Benchmarks | ❌ Not started | No formal benchmark suites. PerformanceGuardrails exist (parse <5ms, template <10ms, render <16ms budgets) but no automated benchmarks |
| 5F. CHANGELOG | ❌ Not started | No CHANGELOG.md |
| 5G. MIGRATION Guide | ✅ Done | MIGRATION.md in repo root |
| 5H. README | ✅ Done | Comprehensive README with architecture, build instructions, demo |
Phase 6: Web/Desktop Spec Parity Gaps — 100% ✅
Status: Complete
These are features that were present in the official Adaptive Cards spec (adaptivecards.io) and the web/desktop SDK that were missing or incomplete in the mobile SDK. All have been implemented.
P0 — Functional Gaps
| Item | iOS | Android | Effort | Description |
|---|---|---|---|---|
| 6A. fallback + requires mechanism | ✅ | ✅ | Medium | FeatureFlags.meetsRequirements() evaluates element requires against host capabilities; rendering pipeline renders fallback element or drops. .unknown elements also render their fallback. |
| 6B. Data.Query (dynamic typeahead) | ✅ | ✅ | Medium | DataQuery model, DataQueryProvider protocol/interface, choicesData on ChoiceSetInput/InputChoiceSet. Host implements provider for search-as-you-type. |
| 6C. CaptionSource on Media | ✅ | ✅ | Small | CaptionSource model (mimeType, url, label) added to Media on both platforms. |
P1 — Property Rendering Gaps
| Item | iOS | Android | Effort | Description |
|---|---|---|---|---|
| 6D. TextBlock.style (heading/columnHeader) | ✅ | ✅ | Small | heading renders with large+bold, columnHeader with default+bold. Accessibility header trait added. columnHeader case added to TextBlockStyle enum. |
| 6E. BackgroundImage repeat modes | ✅ | ✅ | Medium | iOS uses .resizable(resizingMode: .tile). Android uses Canvas with painter tiling. repeat, repeatHorizontally, repeatVertically all work. |
| 6F. Input labelPosition/labelWidth | ✅ | ✅ | Small | labelPosition ("inline"/"above") and labelWidth (string or number) added to all 6 input types on both platforms. Flexible decoder handles both string and numeric labelWidth. |
| 6G. Refresh auto-refresh logic | ✅ | ✅ | Medium | onRefreshNeeded callback on AdaptiveCardView. Timer fires at refresh.expires timestamp. iOS uses .task(id:), Android uses LaunchedEffect + delay(). |
P2 — Low Priority / Host Responsibility
| Item | Status | Notes |
|---|---|---|
| Authentication flow | Model only | Correctly deferred to host app |
| speak (SSML) | Model only | Host responsibility per spec |
| lang | Model only | Could enhance date formatting; minimal impact |
| Input.inputStyle (revealOnHover) | N/A | No hover on mobile — can skip |
Phase 7: Desktop R4 Parity — 100% ✅
Status: Complete — identified via WorkIQ research on David Claux's desktop AC team (March 2026), implemented March 14, 2026
These are features shipped or in-progress on the desktop/web renderer (R4 release) that were parity gaps with the mobile SDK. All have been implemented.
Desktop R4 Feature Gaps
| Item | iOS | Android | Effort | Description |
|---|---|---|---|---|
| 7A. Scrollable Containers | ✅ | ✅ | Medium | maxHeight + overflow (scroll/hidden/visible) on Container, Column, TableCell. iOS: OverflowModifier with ScrollView/.clipped(). Android: verticalScroll/clipToBounds. |
| 7B. Popover Drawer Resizing | ✅ | ✅ | Small | Content-measured detents capped at 80% screen height. iOS: GeometryReader → presentationDetents. Android: skipPartiallyExpanded=false + wrapContentHeight() + heightIn(max=80%). |
| 7C. Streaming Fade-in Animation | ✅ | ✅ | Small | Fade-in via .transition(.opacity) (iOS) / AnimatedVisibility(fadeIn) (Android). Composable-lambda elementRenderer for full element rendering without circular module dependency. |
| 7D. Card Diagnostics Overlay | ✅ | ✅ | Medium | DiagnosticsOverlayView (iOS) / DiagnosticsOverlay (Android) — floating badge + expandable panel. Enabled via CardConfiguration.diagnosticsEnabled. |
7A. Scrollable Containers — Engineering Detail
Problem: Desktop containers support maxHeight with vertical scrollbar. Mobile containers only support minHeight, never scroll.
Model changes — Add to Container, Column, TableCell on both platforms:
maxHeight: String?(e.g. "200px") — parsed same as existingminHeightoverflow: Overflow?— new enum:visible(default),hidden,scroll
Files:
- iOS model:
ios/Sources/ACCore/Models/ContainerTypes.swift+Enums.swift - Android model:
android/ac-core/.../models/CardElement.kt - iOS rendering:
ios/Sources/ACRendering/Views/ContainerView.swiftoverflow == .scroll→ScrollView(.vertical, showsIndicators: true) { content }.frame(maxHeight: maxH)overflow == .hidden→content.frame(maxHeight: maxH).clipped()
- Android rendering:
android/ac-rendering/.../composables/ContainerView.ktOverflow.Scroll→Box(Modifier.heightIn(max = maxHeight)) { Column(Modifier.verticalScroll(rememberScrollState())) { items } }Overflow.Hidden→Box(Modifier.heightIn(max = maxHeight).clipToBounds()) { items }
- Accessibility:
accessibilityScrollView(iOS) / scroll semantics (Android) - Test card: NEW
shared/test-cards/element-samples/container-scrollable.json
7B. Popover Drawer Resizing — Engineering Detail
Problem: iOS always .presentationDetents([.large]). Android skipPartiallyExpanded = true. Desktop: resize to content with max height.
No model changes — rendering only.
Files:
- iOS:
ios/Sources/ACRendering/Views/PopoverContentView.swift- Replace
.presentationDetents([.large])with content-measured detent @State private var contentHeight: CGFloat = 0- Measure via
GeometryReaderon content →.presentationDetents([.height(min(contentHeight + 80, screenHeight * 0.8)), .large]) - Add
.presentationContentInteraction(.scrolls)for tall content
- Replace
- Android:
PopoverBottomSheetinandroid/ac-rendering/.../composables/ActionSetView.kt- Change
skipPartiallyExpanded = true→false - Add
Modifier.wrapContentHeight()to content Column - Cap height:
.heightIn(max = (screenHeightDp * 0.8f).dp)
- Change
7C. Streaming Fade-in Animation — Engineering Detail
Problem: StreamingCardView is a stub on both platforms — shows placeholder text instead of actual rendered elements. Desktop has fade-in animation.
No model changes — rendering only.
Files:
- iOS:
ios/Sources/ACCopilotExtensions/StreamingCardView.swift- Replace stub
Text("Element: ...")withElementView(element:hostConfig:) - Add
.transition(.opacity.animation(.easeIn(duration: 0.3)))per element - Animate on
partialContent.countchanges
- Replace stub
- Android:
android/ac-copilot-extensions/.../StreamingCardView.kt- Replace stub with
RenderElement(element = element, ...) - Wrap in
AnimatedVisibility(visible = true, enter = fadeIn(tween(300)))
- Replace stub with
- Test card: Existing
shared/test-cards/streaming-card.json
7D. Card Diagnostics Overlay — Engineering Detail
Problem: Desktop has Ctrl+Alt+Shift+double-click diagnostic tool. Mobile has CardPerformanceMetrics + CardLifecycleEvent callbacks but no visual inspector.
Model changes: Add diagnosticsEnabled: Bool = false to CardConfiguration on both platforms.
Files:
- iOS: NEW
ios/Sources/ACRendering/Views/DiagnosticsOverlayView.swift- Floating badge (element count + render time) → expandable panel
- 4 tabs: Performance (all
CardPerformanceMetricsfields), Elements (hierarchical tree), JSON (pretty-printed), Errors (unknownElementTypes, failedImageUrls) - Show as
.overlay(alignment: .topTrailing)onAdaptiveCardViewwhenconfiguration.diagnosticsEnabled - Modify
AdaptiveCardView.swiftandCardConfiguration.swift
- Android: NEW
android/ac-rendering/.../composables/DiagnosticsOverlay.kt- Same pattern using
Boxoverlay - Modify
AdaptiveCardView.ktandCardConfiguration.kt
- Same pattern using
Mobile Ahead of Desktop
These elements are already implemented on mobile but still in development on desktop:
| Element | Mobile Status | Desktop Status |
|---|---|---|
| ProgressBar | ✅ Shipped | In development |
| ProgressRing/Spinner | ✅ Shipped | In development |
| Badge | ✅ Shipped (CompoundButton) | Canary |
| Carousel | ✅ Shipped | Canary |
Test Card Coverage
| Gap | Existing Test Card | Needs New Card |
|---|---|---|
| 7A. Scrollable Containers | ❌ | Yes — element-samples/container-scrollable.json |
| 7B. Popover Resizing | ❌ (use existing popover cards) | Optional — tall-content popover card |
| 7C. Streaming Animation | ✅ streaming-card.json | No |
| 7D. Diagnostics | N/A | Any card works |
Files Changed Summary
| Gap | iOS Files | Android Files | New Files |
|---|---|---|---|
| 7A (Scrollable) | ContainerTypes.swift, Enums.swift, ContainerView.swift | CardElement.kt, ContainerView.kt | — |
| 7B (Popover) | PopoverContentView.swift | ActionSetView.kt | — |
| 7C (Streaming) | StreamingCardView.swift | StreamingCardView.kt | — |
| 7D (Diagnostics) | CardConfiguration.swift, AdaptiveCardView.swift | CardConfiguration.kt, AdaptiveCardView.kt | DiagnosticsOverlayView.swift, DiagnosticsOverlay.kt |
Implementation Sequence
| Phase | Gaps | Est. Hours |
|---|---|---|
| P7.1: Desktop layout features | 7A (Scrollable), 7B (Popover) | 6-9h |
| P7.2: Streaming + tooling | 7C (Streaming), 7D (Diagnostics) | 9-12h |
API impact: All changes additive — new optional properties with nil/false defaults. No breaking changes. Hosts opt-in to new features (e.g., diagnosticsEnabled).
Overall Completion Summary
| Phase | Status | Completion |
|---|---|---|
| Phase 1: Templating Engine | ✅ Complete | 100% |
| Phase 2: Advanced Elements + Markdown + Theming | ✅ Complete | 100% |
| Phase 3: Advanced Actions + Copilot + Teams | ✅ Complete | 100% |
| Phase 4: Sample Apps | ✅ Complete | 100% |
| Phase 5: Production Readiness | 🚧 In Progress | 45% |
| Phase 6: Web/Desktop Spec Parity Gaps | ✅ Complete | 100% |
| Phase 7: Desktop R4 Parity | ✅ Complete | 100% |
Overall Feature Completeness vs Official Spec: ~99% (property-level, verified in source code) Overall Feature Completeness vs Teams Extended Spec: ~99% (mobile has additional Teams/Copilot extensions) Overall Feature Completeness vs Desktop R4: ~99% (all 4 desktop R4 gaps closed — scrollable containers, popover resizing, streaming animation, diagnostics)
Remaining Effort Estimate
| Phase | Estimated Hours |
|---|---|
| Phase 5 remaining (publishing, docs, benchmarks, changelog) | 15-20 hours |
| Total remaining | 15-20 hours (Phase 5 only) |
Success Criteria
Functional Requirements
- Templating works with all expression types
- All 43+ test cards render correctly on both platforms
- 100% cross-platform naming consistency
- Full accessibility compliance (WCAG 2.1 AA)
- Responsive design on phone and tablet
- fallback + requires mechanism working
- Data.Query dynamic typeahead working
- Scrollable containers (Desktop R4 parity)
- Streaming fade-in animation (Desktop R4 parity)
- Popover drawer resizing (Desktop R4 parity)
- Card diagnostics overlay (Desktop R4 parity)
Quality Requirements
- Graceful error handling (never crash on malformed JSON)
- <16ms render time for all cards (guardrails exist, benchmarks not automated)
- 80%+ code coverage (tests exist, coverage reporting not in CI)
- All public APIs documented (DocC/Dokka)
Publishing Requirements
- iOS Package.swift ready for SPM (products defined)
- Android modules ready for Maven Central
- Generated API documentation
- Migration guide from legacy SDK
- Sample apps demonstrating features
Risk Mitigation
Technical Risks
fallback mechanism complexity: RESOLVED — implemented and working on both platformsData.Query host integration: RESOLVED —DataQueryProviderprotocol/interface implementedScrollable containers: RESOLVED —OverflowModifierwraps nestedScrollViewcleanly; tested on both platformsPopover resizing: RESOLVED —GeometryReaderusesonAppear(not continuous); availability-checkedpresentationContentInteractionwith fallback modifierStreaming animation: RESOLVED — used composable-lambdaelementRendererpattern to avoid circular module dependency (ACCopilotExtensions → ACRendering)- SwiftUI/Compose compatibility: Tested on iOS 16+ and Android API 26+
Schedule Risks
- Phase 5 publishing: Maven Central publishing has bureaucratic steps (Sonatype account, GPG signing)
- DocC/Dokka generation: First-time setup can be time-consuming
Quality Risks
- Accessibility: Tested with VoiceOver/TalkBack throughout development
- Error handling: Never crash on malformed JSON — validated across 43+ test cards
- Thread safety: Parsing on background threads, UI on main thread (verified)