ios – Google IMA SDK: UIViewControllerHierarchyInconsistency – IMAAdViewController mum or dad is null as a substitute of NativeVideoPlayerViewController

ios – Google IMA SDK: UIViewControllerHierarchyInconsistency – IMAAdViewController mum or dad is null as a substitute of NativeVideoPlayerViewController


I am creating an app with a YouTube-like PipView (Image-in-Image) construction for stay broadcasting. The app crashes through the transition from the stay broadcast tab to PipView, following these steps:

  1. LiveBroadcastVC.swift – Line 300 LiveBroadcastVC.handleDragContainerViewGesture(_:) + 300

  2. LiveBroadcastVC.swift – Line 268 LiveBroadcastVC.tabBarFirstView() + 268

  3. UIKitCore-[UIViewController dismissViewControllerAnimated:completion:]

  4. thunk for @escaping @callee_guaranteed @Sendable () -> ()

  5. TabbarVC.swift – Line 124 @objc TabbarVC.selectedIndex.setter + 124

  6. TabbarVC.selectedViewController.didset

  7. PlayerManager.swift – Line 289 PlayerManager.showPipView() + 289

  8. PipView.swift – Line 139 PlayerManager.updatePipViewUI() + 139

  9. PipView.swift – Line 210 PipView.addAvPlayer(_:adContainer:) + 210

And I am getting this error:

Deadly Exception: UIViewControllerHierarchyInconsistency youngster view controller:<IMAAdViewController: 0x1226be800> ought to have mum or dad view controller:(null) however precise mum or dad is:<CNNTurk.NativeVideoPlayerViewController: 0x1097cd800>

The crash happens particularly when:

  • Person is watching a stay broadcast with an advert enjoying
  • Person drags down the stay broadcast view to attenuate it to PipView
  • The app makes an attempt to switch the IMAAdViewController to the brand new container

Here is my code blocks;

func showPipView() {
        guard let window = UIApplication.shared.keyUIWindow else { return }

        if pipView == nil {
            pipView = PipView(onTap: didTapPipView,
                              onPause: didTapPauseButton,
                              onClose: resetPlayerObjects)
            window.addSubview(pipView!)

            pipView?.translatesAutoresizingMaskIntoConstraints = false
            pipViewBottomConstraint = pipView?
                .bottomAnchor
                .constraint(equalTo: window.bottomAnchor,
                            fixed: pipViewBottomConstant)
            pipViewBottomConstraint?.isActive = true
            pipView?.updateBottomViewIsHidden(isAppearTabBar)
            pipView?.widthAnchor.constraint(equalTo: window.widthAnchor).isActive = true
            window.bringSubviewToFront(pipView!)
            updatePipViewUI()
        }
    }

personal func updatePipViewUI() {
        guard let videoLayerType,
              let currentPlayerLayer else { return }
        pipView?.addPlayerLayer(currentPlayerLayer, adContainer: currentPlayer?.adContainer)
        pipView?.updateTitle(videoLayerType.title, subtitle: videoLayerType.subtitle)
        pipView?.updatePauseButtonImage(isPlaying: isPlaying)
    }

func addPlayerLayer(_ playerLayer: Any, adContainer: AnyObject?) {
        swap playerLayer {
        case let avPlayer as AVPlayer:
            addAvPlayer(avPlayer, adContainer: adContainer)
        default: fatalError()
        }
    }

func addAvPlayer(_ avPlayer: AVPlayer, adContainer: AnyObject?) {
        let playerLayer = AVPlayerLayer(participant: avPlayer)
        playerLayer.videoGravity = .resizeAspectFill
        playerView.layer.addSublayer(playerLayer)
        playerView.layoutIfNeeded()
        playerLayer.body = CGRect(origin: .zero, measurement: playerView.bounds.measurement)

        if PlayerManager.shared.isPlayingAd {
            if let adView = (adContainer as? UIViewController)?.view.subviews.final {
                adView.translatesAutoresizingMaskIntoConstraints = false
                playerView.addSubview(adView)
                PlayerManager.shared.currentPlayer?.adContainer = adView
                adView.leadingAnchor.constraint(equalTo: playerView.leadingAnchor).isActive = true
                adView.topAnchor.constraint(equalTo: playerView.topAnchor).isActive = true
                adView.trailingAnchor.constraint(equalTo: playerView.trailingAnchor).isActive = true
                adView.bottomAnchor.constraint(equalTo: playerView.bottomAnchor).isActive = true
            } else if let adView = adContainer as? UIView {
                playerView.addSubview(adView)
                adView.backgroundColor = .clear
                adView.leadingAnchor.constraint(equalTo: playerView.leadingAnchor).isActive = true
                adView.topAnchor.constraint(equalTo: playerView.topAnchor).isActive = true
                adView.trailingAnchor.constraint(equalTo: playerView.trailingAnchor).isActive = true
                adView.bottomAnchor.constraint(equalTo: playerView.bottomAnchor).isActive = true
            }
        }
    }

How can I correctly handle the view controller hierarchy when transitioning the Google IMA adverts view controller between containers? Any assist can be appreciated.

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 
rooshohttps://www.roosho.com
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog. 

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here


Latest Articles

author avatar
roosho Senior Engineer (Technical Services)
I am Rakib Raihan RooSho, Jack of all IT Trades. You got it right. Good for nothing. I try a lot of things and fail more than that. That's how I learn. Whenever I succeed, I note that in my cookbook. Eventually, that became my blog.