Migration Guide

March 13, 2026 · View on GitHub

This guide helps you migrate from the legacy Adaptive Cards SDK to the new mobile-optimized SDK.

Overview

The new SDK provides:

  • Modern UI frameworks (SwiftUI, Jetpack Compose)
  • Modular architecture for smaller app size
  • Enhanced performance and memory efficiency
  • Better accessibility support
  • Complete Adaptive Cards v1.6 support (including Table, Badge, Icon, CompoundButton)
  • Improved developer experience

Breaking Changes

Architecture

Legacy SDK

  • Monolithic UIKit/View-based rendering
  • Tight coupling between parsing and rendering
  • Limited customization options

New SDK

  • Modular packages/libraries
  • Clean separation of concerns
  • Extensive customization via host config

Platform Requirements

PlatformLegacy SDKNew SDK
iOSiOS 11+iOS 16+
AndroidAPI 21+API 26+
Swift4.2+5.9+
Kotlin1.4+1.9+

Migration Steps

iOS Migration

1. Update Dependencies

Before (CocoaPods):

pod 'AdaptiveCards', '~> 2.8'

After (Swift Package Manager):

dependencies: [
    .package(url: "https://github.com/VikrantSingh01/AdaptiveCards-Mobile", from: "1.0.0")
],
targets: [
    .target(
        name: "YourApp",
        dependencies: [
            .product(name: "ACCore", package: "AdaptiveCards-Mobile"),
            .product(name: "ACRendering", package: "AdaptiveCards-Mobile"),
        ]
    )
]

2. Update Imports

Before:

import AdaptiveCards

After:

import ACCore
import ACRendering

3. Rendering Cards

Before (UIKit):

let cardView = ACRView(frame: bounds, card: card, hostConfig: hostConfig, target: self)
view.addSubview(cardView)

After (SwiftUI):

struct ContentView: View {
    let card: AdaptiveCard
    
    var body: some View {
        AdaptiveCardView(card: card, hostConfig: .default) { action in
            handleAction(action)
        }
    }
}

4. Action Handling

Before:

extension MyViewController: ACRActionDelegate {
    func didFetchUserResponses(_ card: ACOAdaptiveCard!, 
                               action: ACOBaseActionElement!) {
        // Handle action
    }
}

After:

AdaptiveCardView(card: card) { action in
    switch action {
    case .submit(let data):
        handleSubmit(data)
    case .openUrl(let url):
        UIApplication.shared.open(url)
    default:
        break
    }
}

5. Custom Styling

Before:

let hostConfig = ACOHostConfig()
hostConfig.setFontFamily(.body, fontFamily: "SF Pro")
hostConfig.setFontSize(.default, fontSize: 14)

After:

var hostConfig = HostConfig.default
hostConfig.fontFamily = "SF Pro"
hostConfig.fontSize = .medium
hostConfig.spacing = .default

Android Migration

1. Update Dependencies

Before (Gradle):

dependencies {
    implementation("io.adaptivecards:adaptivecards-android:2.8.0")
}

After (Gradle):

dependencies {
    implementation("com.microsoft.adaptivecards:ac-core:1.0.0")
    implementation("com.microsoft.adaptivecards:ac-rendering:1.0.0")
}

2. Update Imports

Before:

import io.adaptivecards.objectmodel.AdaptiveCard
import io.adaptivecards.renderer.AdaptiveCardRenderer

After:

import com.microsoft.adaptivecards.core.AdaptiveCard
import com.microsoft.adaptivecards.rendering.AdaptiveCardComposable

3. Rendering Cards

Before (View-based):

val cardView = AdaptiveCardRenderer.getInstance().render(
    context,
    fragmentManager,
    adaptiveCard,
    cardActionHandler,
    hostConfig
)
linearLayout.addView(cardView)

After (Jetpack Compose):

@Composable
fun CardScreen(card: AdaptiveCard) {
    AdaptiveCardComposable(
        card = card,
        hostConfig = HostConfig.Default,
        onAction = { action ->
            handleAction(action)
        }
    )
}

4. Action Handling

Before:

val cardActionHandler = object : BaseCardElementRenderer() {
    override fun onAction(action: BaseActionElement, renderedCard: RenderedAdaptiveCard) {
        when (action) {
            is SubmitAction -> handleSubmit(action.getDataJson())
        }
    }
}

After:

AdaptiveCardComposable(
    card = card,
    onAction = { action ->
        when (action) {
            is Action.Submit -> handleSubmit(action.data)
            is Action.OpenUrl -> openUrl(action.url)
            else -> {}
        }
    }
)

5. Custom Styling

Before:

val hostConfig = HostConfig().apply {
    setFontFamily(FontType.Default, "Roboto")
    setFontSize(FontSize.Default, 14)
}

After:

val hostConfig = HostConfig.Default.copy(
    fontFamily = "Roboto",
    fontSize = FontSize.Medium,
    spacing = Spacing.Default
)

Feature Mapping

Elements

Legacy NameNew NameNotes
TextBlockTextBlockSame, enhanced styling options
ImageImageAdded themed variants support
ContainerContainerSame
ColumnSetColumnSetSame
FactSetFactSetSame
N/AList✨ New: Advanced list container
N/ACarousel✨ New: Image carousel
N/AAccordion✨ New: Collapsible sections
N/ATabSet✨ New: Tabbed content
N/ATable✨ New: Data tables
N/ADataGrid✨ New: Advanced data grid

Inputs

Legacy NameNew NameChanges
Input.TextInput.TextEnhanced validation
Input.NumberInput.NumberSame
Input.DateInput.DateSame
Input.TimeInput.TimeSame
Input.ToggleInput.ToggleSame
Input.ChoiceSetInput.ChoiceSetEnhanced styling

Actions

Legacy NameNew NameChanges
Action.OpenUrlAction.OpenUrlSame
Action.SubmitAction.SubmitEnhanced data collection
Action.ShowCardAction.ShowCardSame
Action.ToggleVisibilityAction.ToggleVisibilitySame
N/AAction.Execute✨ New: Custom command execution
N/ACompoundButton✨ New: Multi-action buttons
N/ASplitButton✨ New: Split action buttons
N/APopoverAction✨ New: Popover menus

Common Migration Issues

Issue 1: Card Not Rendering

Problem: Card appears blank or doesn't render

Solution: Ensure you're using the correct rendering method for your UI framework

// iOS - Use SwiftUI View
AdaptiveCardView(card: card, hostConfig: .default)

// Android - Use Composable
AdaptiveCardComposable(card = card, hostConfig = HostConfig.Default)

Issue 2: Actions Not Firing

Problem: Button actions don't trigger callbacks

Solution: Ensure action handler is properly connected

// iOS
AdaptiveCardView(card: card) { action in
    print("Action: \(action)")  // Add logging
    handleAction(action)
}

// Android
AdaptiveCardComposable(
    card = card,
    onAction = { action ->
        Log.d("Card", "Action: $action")  // Add logging
        handleAction(action)
    }
)

Issue 3: Styling Not Applied

Problem: Custom host config doesn't affect card appearance

Solution: Verify host config is passed correctly and uses new API

// iOS
var config = HostConfig.default
config.fontFamily = "CustomFont"
config.accentColor = .blue

AdaptiveCardView(card: card, hostConfig: config)

// Android
val config = HostConfig.Default.copy(
    fontFamily = "CustomFont",
    accentColor = Color.Blue
)
AdaptiveCardComposable(card = card, hostConfig = config)

Issue 4: Performance Issues

Problem: Slow rendering or high memory usage

Solution:

  1. Use modular imports (import only what you need)
  2. Enable caching for repeated cards
  3. Profile with the built-in performance dashboard
// iOS - Use specific modules
import ACCore  // Just core models
import ACRendering  // Just rendering

// Android - Use specific dependencies
implementation("com.microsoft.adaptivecards:ac-core:1.0.0")
implementation("com.microsoft.adaptivecards:ac-rendering:1.0.0")

Testing Your Migration

Validation Checklist

  • All cards render correctly in light mode
  • All cards render correctly in dark mode
  • All input elements are functional
  • All actions trigger expected behavior
  • Custom styling is applied correctly
  • Accessibility features work (VoiceOver/TalkBack)
  • Performance is acceptable (parse < 5ms, render < 10ms)
  • Memory usage is within limits
  • Error handling works for invalid cards
  • Network images load correctly

Test Cards

Use the included test cards in shared/test-cards/ to validate:

  • simple-text.json - Basic rendering
  • all-inputs.json - Input validation
  • all-actions.json - Action handling
  • containers.json - Layout
  • advanced-combined.json - Complex scenarios

Getting Help

Resources

Support

If you encounter issues during migration:

  1. Check this guide for solutions
  2. Review the sample apps for working examples
  3. Search existing GitHub issues
  4. Create a new issue with:
    • Legacy SDK version
    • New SDK version
    • Minimal reproduction code
    • Error messages/logs

Timeline Recommendations

App ComplexityEstimated Migration Time
Simple (1-5 card types)1-2 days
Medium (6-15 card types)3-5 days
Complex (15+ card types, custom elements)1-2 weeks

Plan for additional time to test thoroughly across devices and OS versions.