heap / heap-ios-autocapture-sdk

Other
3 stars 0 forks source link

Autocapture not working with custom container view controller layouts #3

Closed nathreed closed 1 year ago

nathreed commented 1 year ago

Our app makes heavy use of container view controller-based layouts - for example in one layout, we have a segmented control that determines which of 4 child view controllers are shown in the main content area.

The new Autocapture SDK is not properly capturing views of the child view controllers - instead we get views of the container VC (which we don't actually care about). The legacy iOS SDK correctly captured the views of the child view controllers, and we haven't changed anything with our layout since upgrading to the new autocapture SDK. For this particular situation, we tried defining events based on user taps of the segmented control instead, but the numbers for those are much lower than the numbers we see coming in from previous versions of the app that had the legacy SDK.

We'd really like to avoid having to create custom code-driven events for all these page views (most of the value proposition for Heap in our org is that the product team can create new events with minimal engineering effort). Could you please confirm whether capturing views on child view controllers is currently supposed to work with the new autocapture SDK, and if not, whether that's on your roadmap?

bnickel commented 1 year ago

This change was intentional and actually something that was applied in version 9.0 of the Classic SDK. The previous behavior treated each view controller as a pageview which had a severe negative impact on apps that use child view controllers for components rather than for hosting significant view controllers. There were also issues of pageview attribution in apps with UISplitViewController or multiple screens where touches were always attributed to the last appearing view controller rather than the one the touch occurred in.

The new SDK limits pageviews to presented view controllers and children of UISplitViewController, UINavigationController, UITabBarController, and UIPageViewController by default.

This is driven by a publicly accessible, but not yet documented properties:

extension UIViewController {

    /// If set to true, this class will be considered a container view controller
    /// for Heap pageviews, meaning any direct child view controllers will be issue
    /// pageviews on appearance.
    ///
    /// The default value is false.
    @objc open class var isHeapContainerClass: Bool { get }

    /// If set to true, this instance will behave as a container view controller
    /// for Heap pageviews, meaning any direct child view controllers will be issue
    /// pageviews on appearance.
    ///
    /// The default value is defined by the static `isHeapContainerClass` property
    /// and will be `true` if the class inherits from one of the built-in container
    /// view controllers. (E.g., `UINavigationController` or `UISplitViewController`)
    @IBInspectable open var isHeapContainer: Bool { get set }

    /// The view controller to attribute touch events to, if the current view
    /// view controller is a container and receives touches.
    ///
    /// This is used for container view controllers that contain their own interactive
    /// elements, such as `UINavigationController` or `UITabBarController`. If a
    /// touch is received on one of these view controllers, this property will be used
    /// to attribute the event to the child view controller.
    @objc open var heapChildViewControllerForPageviews: UIViewController? { get }
}

Since it sounds like you have a segmented controller inside the container view and outside the child views, you'll want to do something similar to what we do for UINavigationController:

import HeapIOSAutocapture

extension YourContainerViewController {

    open override class var isHeapContainerClass: Bool {
        true
    }

    open override var heapChildViewControllerForPageviews: UIViewController? {
        propertyForTheActiveChild // Or children.first
    }
}

The first property will (1) suppress pageviews on YourContainerViewController and (2) add cause the child view controllers to receive pageviews. The second property will cause touches on the segmented controller to be attributed to the child's pageview rather than the last created pageview.

You can get a similar effect setting isHeapContainer on the instance or in Interface Builder but there's no guarantee that the segment control's pageviews will always be accurate.


My two takeaways are:

  1. We need to get up documentation about our pageview behavior and how to customize it.
  2. The current behavior where we don't capture an appearance event at all for child view controllers is insufficient.

On Android, we have a concept of a Fragment Transition Event, which is distinct from a pageview and compensates for the fact we can't say a fragment is a pageview but can't say that it isn't. Creating a similar event for child pageviews probably makes sense here as well.

I'll take these notes back to the team.

Thanks, Brian

nathreed commented 1 year ago

I really appreciate your quick and comprehensive response. The number of knobs and dials that the new SDK has for customization that it seems like the legacy one didn't (or at least, that were documented) is quite impressive.

I tried what you suggested and indeed it's working how I expect now. Thanks also for the reminder to check the interfaces on HeapIOSAutocaptureImplementation for properties that you all have exposed and documented in headers but haven't documented online yet - that's definitely something I hadn't done yet because of the jump from HeapIOSAutocapture -> HeapIOSAutocaptureImplementation required to see the "good stuff".