microsoft / AdaptiveCards

A new way for developers to exchange card content in a common and consistent way.
https://adaptivecards.io
MIT License
1.75k stars 550 forks source link

[iOS][Custom Rendering] Crash occurs when using custom input renderer in Swift #3908

Closed JohnnyKehr closed 4 years ago

JohnnyKehr commented 4 years ago

Platform

What platform is your issue or question related to? (Delete other platforms).

Author or host

Host on Webex Teams

Version of SDK

AdaptiveCards@v1.2.6

Details

  1. Given a basic custom text input renderer implementation (copied almost exactly from the examples in the documentation):
import UIKit

class CustomTextInputRenderer: ACRBaseCardElementRenderer {
    override func render(_ viewGroup: (UIView & ACRIContentHoldingView)!, rootView: ACRView!, inputs: [Any]!, baseCardElement acoElem: ACOBaseCardElement!, hostConfig acoConfig: ACOHostConfig!) -> UIView! {
        let renderer = ACRInputRenderer.getInstance()
        let input = renderer?.render(viewGroup, rootView: rootView, inputs: inputs, baseCardElement: acoElem, hostConfig: acoConfig)
        if let input = input {
            input.backgroundColor = .red
        }
        return input
    }
}
  1. And then registering it in the view that's presenting the rendered card:
private let customTextInputRenderer = CustomTextInputRenderer()

override init(frame: CGRect) {
    super.init(frame: frame)

    let registration = ACRRegistration.getInstance()
    registration.setBaseCardElementRenderer(customTextInputRenderer, cardElementType: .textInput)
}
  1. And then rendering a card with the native text input component, such as the one taken from the examples: https://adaptivecards.io/samples/Inputs.html

  2. Will result in a crash when the card is being rendered (I've sanitized some of these stack traces and have left only the relevant bits):

2020-04-07 14:54:09.561447-0700 Teams[44818:248673] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Swift.__EmptyArrayStorage addObject:]: unrecognized selector sent to instance 0x10939eb68'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff23c7127e __exceptionPreprocess + 350
    1   libobjc.A.dylib                     0x00007fff513fbb20 objc_exception_throw + 48
    2   CoreFoundation                      0x00007fff23c91fd4 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
    3   CoreFoundation                      0x00007fff23c75c4c ___forwarding___ + 1436
    4   CoreFoundation                      0x00007fff23c77f78 _CF_forwarding_prep_0 + 120
    5   AdaptiveCards                       0x000000010710414e -[ACRInputRenderer render:rootView:inputs:baseCardElement:hostConfig:] + 10702
    6   Teams                               0x0000000103f7c82f $s5Custom0A17TextInputRendererC6render_8rootView6inputs15baseCardElement10hostConfigSo6UIViewCSgSo018ACRIContentHoldingG0_AJXcSg_So7ACRViewCSgSayypGSgSo07ACOBasejK0CSgSo07ACOHostM0CSgtF + 607
    7   Teams                               0x0000000103f7cab3 $s5Custom0A17TextInputRendererC6render_8rootView6inputs15baseCardElement10hostConfigSo6UIViewCSgSo018ACRIContentHoldingG0_AJXcSg_So7ACRViewCSgSayypGSgSo07ACOBasejK0CSgSo07ACOHostM0CSgtFTo + 275
    8   AdaptiveCards                       0x0000000107143589 +[ACRRenderer render:rootView:inputs:withCardElems:andHostConfig:] + 1625
    9   AdaptiveCards                       0x000000010714228a +[ACRRenderer renderWithAdaptiveCards:inputs:context:containingView:hostconfig:] + 2106
    10  AdaptiveCards                       0x00000001070b0370 -[ACRView render] + 560
    11  AdaptiveCards                       0x00000001070afea7 -[ACRView init:hostconfig:widthConstraint:delegate:] + 1463
    12  AdaptiveCards                       0x00000001071413bd +[ACRRenderer render:config:widthConstraint:delegate:] + 237
    13  Teams                               0x000000010398b960 $<removed> + 1440
    14  Teams                               0x0000000103904a14 $<removed> + 5796
    15  Teams                               0x00000001039c121a $<removed> + 2410
    16  Teams                               0x00000001039c1aa5 $<removed> + 165
)
libc++abi.dylib: terminating with uncaught exception of type NSException

Hypothesis: When overriding the render function from the base class (ACRBaseCardElementRenderer) in Swift, you are given this signature:

override func render(_ viewGroup: (UIView & ACRIContentHoldingView)!, rootView: ACRView!, inputs: [Any]!, baseCardElement acoElem: ACOBaseCardElement!, hostConfig acoConfig: ACOHostConfig!) -> UIView!

However, Swift arrays cannot be cast into NSMutableArrays because Swift arrays are structs. Since swift types like Tuple or Struct have no equivalent in Objective-C they can not be cast to or referenced as AnyObject which NSArray and NSMutableArray constrain their element types to. Indeed, when I put a breakpoint in the first line of the render function in ACRInputRenderer and print out inputs I get this:

(lldb) po inputs
<Swift.__EmptyArrayStorage 0x10f8bab68>(

)

Note that all my examples are using ACRInputRenderer, but this crash should happen in any renderer that modifies/adds to input in any way.

jwoo-msft commented 4 years ago

@JohnnyKehr,

Thanks for reporting.

Could you try the branch, jwoo/ios-swift-input in this PR?

Thank you

JohnnyKehr commented 4 years ago

Hey Joseph -- I tried out your change and the crash no longer occurs (and inputs are still passed along as expected). I appreciate the quick turnaround!

Johnny

jwoo-msft commented 4 years ago

Hey Joseph -- I tried out your change and the crash no longer occurs (and inputs are still passed along as expected). I appreciate the quick turnaround!

Johnny

It's my pleasure

ghost commented 4 years ago

:tada:AdaptiveCards@v1.2.9 has been released which fixes this issue.:tada:

Handy links: