FAQ & Troubleshooting

Common Mistakes

This section contains common mistakes that can lead to a FeedViewController to not behave correctly.

View Controller Lifecycle Management

Properly managing a view controller's lifecycle is easily overlooked because Xcode does not strictly enforce this. So when a subtle issue does arise the root cause may not be all that obvious and is hard to debug.

Attaching a View Controller

When add a view controller as a child of another view controller it is important to call the correct methods so that it will start to receive the normal view controller life cycle events.

The steps for attaching a child view controller are

  1. Add child to parent view controller

  2. Add add root view from child view controller to some view in the parent view controller

  3. Set constraints on child view controller view

  4. Inform the child view controller that it has been moved onto the parent view controller

The last step is really important and also overlooked as it will inform the child view controller that it that it can begin laying out its components.

Here is an example

let feedVC = //... 

parentViewController.addChild(feedVC)

parentViewController.view.addSubView(feedVC.view)

feedVC.view.translatesAutoresizingMaskIntoConstraints = false

// ... Set feed constraint
NSLayoutConstraint.activate(
    constraints
)

feedVC.didMove(toParent: parentViewController)

Detaching a View Controller

Similar to attaching a view controller we must also detach a view controller. Detaching is more straightforeward.

The steps to detach

  1. Inform the child view controller it will be moving from the parent view controller

  2. Remove the child view controller from the parent

  3. remove the child view controller view from super view

feedVC.willMove(toParent: nil) // Let's the child VC know it will be detached from the parent

feedVC.removeFromParent()

feedVC.view.removeSuperview()

Attaching a View Controller in a Table or Collection Cell View

Often times it is desirable to display a horizontal scrolling feed within a UITableView or UICollectionView.

There are two lifecycle events on the UITableView and UICollectionView that we can use to attach and deatch the FeedViewController. For details on attaching and detaching see View Controller Lifecycle Management section above.

The Table and Collection views have a delegate method that is called before a cell is going to be shown on a screen; table/collectionView(_:willDisplayCell:at:). When this method is called it is a good time to attach the feed view controller.

Conversely, the Table and Collection views have a method that is called just after a cell has left the screen; table/collectionView(_:didEndDisplayingCell:at:). WHen this method is called it is a good time to detach the feed view controller.

Safe Area Guides

Because the VideoFeedViewController is self contained it must be able to avoid safe areas. As such the VideoFeedViewController attaches its child view controller to the safeAreaGuide of its view.

There are some scenarios that may cause odd behaviors when the VideoFeedViewController is laid out.

Sibling Views

This scenario occurs when a the VideoFeedViewController is attached to a UIScrollView, UICollectionView or UITableView which has a sibling view. The safe area guides will propagate down to the VideoFeedViewController when the UIScrollView, UICollectionView or UITableView is not the first child in the view hierarchy.

Typically, UIScrollView, UICollectionView or UITableView respects the safe area by adjusting the content size to allow the content to scroll above the safe area. Thus, UIKit does not pass the guides down into views that are within the content view of the UIScrollView. However, behavior suggests there is a bug within the UIKit logic.

Workarounds

To get around this issue simply make sure the UIScrollView, UICollectionView or UITableView is the first sibling of the parent view. In story board simply drag the view in question to the top of the subclasses. Or, if you are building views programmatically simply make sure to view.addSubview() the view in question first.

SDK Initialization

FireworkVideoSDK.initializeSDK accepts an optional delegate parameter that can receive any errors the SDK outputs during setup. This delegate can be any class that conforms to the FireworkVideoSDKDelegate protocol. See example code below that uses AppDelegate to print any errors to console.

import UIKit

/// Add the dependency SDK
import FireworkVideo

@main
class AppDelegate: UIResponder, UIApplicationDelegate, FireworkVideoSDKDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        FireworkVideoSDK.initializeSDK(delegate: self)
        return true
    }
    
    func fireworkVideoDidLoadSuccessfully() {
        print("FireworkVideo loaded successfully.")
    }
    
    func fireworkVideoDidLoadWith(error: FireworkVideoSDKError) {
        switch error {
        case .missingAppID:
            print("FireworkVideo loaded with error due to missing FireworkVideo app ID.")
        case .authenticationFailure:
            print("FireworkVideo loaded with error due to authentication failure.")
        @unknown default:
            break
        }
    }

Video Feeds

VideoFeedViewController exposes an optional delegate property that can receive any errors the SDK outputs when loading the video feed or a success when the video feed loaded successfully.

/// Add the dependency SDK
import FireworkVideo

class VideoFeedService: VideoFeedViewControllerDelegate {

    func videoFeedDidLoadFeed(_ viewController: FireworkVideo.VideoFeedViewController) {
        print("Video feed on view controller \(viewController) loaded.")
    }

    func videoFeed(_ viewController: FireworkVideo.VideoFeedViewController, didFailToLoadFeed error: FireworkVideo.VideoFeedError) {
        if case let FireworkVideo.VideoFeedError.contentSourceError(contentSourceError) =  error {
            print("Video feed on view controller \(viewController) loaded with error \(contentSourceError.errorDescription).")
        } else if case let FireworkVideo.VideoFeedError.unknownError(underlyingError) = error {
            print("Video feed on view controller \(viewController) loaded with error \(underlyingError.localizedDescription).")
        }        
    }
}

Missing or Empty Video Feeds

In the event the feed is empty or the SDK encounters an error when downloading videos, create a new instance and replace the existing instance in your view hierarchy. If the video feed source configured for the view controller does not have videos available, you may need to supply an alternative source when creating the new instance.

If VideoFeedViewController is a child view controller, make sure to call the view controller containment lifecycle methods when removing it from your apps view hierarchy to ensure proper cleanup.

/// Removing videoFeedViewController from it's parent view controller

videoFeedViewController.willMove(toParent: nil)
videoFeedViewController.view.removeFromSuperview()
videoFeedViewController.removeFromParent()

Creating Feed with Circular Thumbnails

A commonly asked question is how to create video feed with circular thumbnails. You can use the following logic to implement this

You can create a circular thumbnails by setting the cornerRadius property in viewConfiguration of a VideoFeedViewController.

Note: This would only work when the number of lines is fixed. This can be done on the VideoFeedItemContentConfiguration.title.numberOfLines

The following properties are needed identify the corner radius required for circular thumbnails.

  1. totalHeight = This will be the height you set for the VideoFeedViewController

  2. contentPadding = This value is found in the layout.contentInsets property of the VideoFeedViewController

  3. labelHeight = The size of the font used on the viewConfiguration.itemView.title.font

  4. titleSpacing = The spacing between the label and the thumbnail. This is located in the viewConfiguration.itemView.titleLayoutConfiguration.insets.top Putting it all together

imageSize = totalHeight - (contentPadding.top + contentPadding.bottom) + (labelHeight * numberOfLines) + titleSpacing

cornerRadius = imageSize / 2 

Now set the cornerRaidus property for your viewConfiguration and you are done!

Last updated