Crash on UICollectionViewCell with JWVideoView - Swift
Asked Answered
A

1

8

A ViewController has a UICollectionView. One of the cells contains JWVideoView. The app is frequently crashing on prepareForReuse in this cell.

There is no valuable info in the log. So I am having trouble figuring out the reason for the crash.

I've created a project example that demonstrates the crash. You can find it https://github.com/fuxlud/JWExample If the link between the cell and the videoView is removed, the crash will not happen.

import UIKit

class VideoArticleElementCollectionViewCell: UICollectionViewCell {

    // MARK: - Properties

    public var imageURL: String? { didSet { videoView?.imageURL = imageURL } }
    public var videoId: String? { didSet { videoView?.videoId = videoId } }

    @IBOutlet private var videoView: JWVideoView?

    // MARK: - Reuse

    override func prepareForReuse() {
        super.prepareForReuse() // Crashing here! (Thread 1: EXC_BAD_ACCESS (code=1, address=0x7e8))

        videoView?.stopPlayingVideo()
    }

    deinit {

        videoView?.stopPlayingVideo()
    }
}








import UIKit

class JWVideoView: UIView, JWPlayerDelegate {

    // MARK: Properties

    public var imageURL: String?
    public var videoId: String? { didSet { setupPlayer() } }

    private var jwPlayer: JWPlayerController?
    private let jwPlayerURL = "https://content.jwplatform.com/manifests/"
    private var didPause = false

    // MARK: - Initialization

    override init(frame: CGRect) {

        super.init(frame: frame)
        setup()
    }

    convenience init() {

        self.init(frame: CGRect.zero)
    }

    required init?(coder aDecoder: NSCoder) {

        super.init(coder: aDecoder)
        setup()
    }

    // MARK: - Setup

    private func setup() {}

    private func setupPlayer() {



            guard let videoId = self.videoId else { return }

            let playerURL = jwPlayerURL + videoId + ".m3u8"

            let configuration: JWConfig = JWConfig(contentURL: playerURL)
            configuration.controls = true
            configuration.autostart = true
//            configuration.premiumSkin = JWPremiumSkinGlow
            configuration.image = imageURL

            jwPlayer = JWPlayerController(config: configuration)

            if let player = jwPlayer {

                player.forceFullScreenOnLandscape = true
                player.forceLandscapeOnFullScreen = true
                player.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
                player.view?.frame = bounds
                player.delegate = self
                player.volume = 0.0
                if let view = player.view { addSubview(view) }
            }

    }

    // MARK: - Orientation

    private func enableAllOrientation(enable: Bool) {

        if let delegate = UIApplication.shared.delegate as? AppDelegate {

//            delegate.shouldEnableLandscape = enable
        }
    }

    // MARK: API

    public func stopPlayingVideo() {

        enableAllOrientation(enable: false)

        if jwPlayer != nil {

            jwPlayer!.stop()
        }
    }

    // MARK: - JWPlayerDelegate

    internal func onFullscreen(_ status: Bool) {

        if status == false {

            let value = UIInterfaceOrientation.portrait.rawValue
            UIDevice.current.setValue(value, forKey: "orientation")
        }
    }

    internal func onPlayAttempt() {

        if jwPlayer != nil {

            enableAllOrientation(enable: true)


        }
    }

    internal func onPlay(_ oldValue: String) {

        if didPause {

            didPause = false
        }
    }

    internal func onPause(_ oldValue: String) {

        didPause = true

    }

    internal func onComplete() {

    }

}
Arouse answered 28/7, 2019 at 7:44 Comment(12)
When I was worked with JWPlayer, I have faced so many concerns or crashes. Trust me it's not our codding related stuff because JWPlayer is having so many known issues. I think you need to talk with the support team for an issue. In my application there 20K crash related to JWPlayer, it is more than the total number of the user. If your commented JWPlayer related code then everything worked as expected.Fivefinger
Thanks, @iMHiteshSurani for your eye-opening and sad comment.Arouse
Sorry to hear that @Luda. Do you know what version of the SDK you're using? In the past couple months we've made significant improvements to our SDKs.Krever
@ksindi, OMG! VP Engineering at JW Player! Awesome for being responsive on StackOverflow. We are currently using pod 'JWPlayer-SDK', '2.8.6'. In addition, I've updated my question with more relevant code. If you prefer we can talk over email.Arouse
@Arouse noticed the version you're using is < 3x. We've made significant stability improvements since then and no longer support 2x. Would it be possible to upgrade to the later version? For how see developer.jwplayer.com/sdk/ios/docs/developer-guide/migration/…. Our API is a lot more Swift friendly.Krever
@ksindi I'll upgradeArouse
@ksindi Unfortuanlty the app is still crashing.Arouse
@Arouse Will the app still crashes if you remove JWVideoView from the cell?Lemoine
could u share the error log pls even if it seems invaluable to u?Culture
@Lemoine I've created a project example that demonstrates the crash. You can find it github.com/fuxlud/JWExample If the link between the cell and the videoView is removed, the crash will not happen.Arouse
@Culture I've created a project example that demonstrates the crash. You can find it github.com/fuxlud/JWExample If the link between the cell and the videoView is removed, the crash will not happen.Arouse
@ksindi I've created a project example that demonstrates the crash. You can find it github.com/fuxlud/JWExample If the link between the cell and the videoView is removed, the crash will not happen.Arouse
W
2

Based on your example project a saw the following issue inside your JWVideoView class: everytime you setting the videoId property it initiliaze the jwPlayer again, and also readds this view again to the stack.

1. Solution (remove the playerView and set the player to nil):

  private func setupPlayer() {

  jwPlayer?.view?.removeFromSuperview()
  jwPlayer = nil

  guard let videoId = self.videoId else { return }

  let playerURL = jwPlayerURL + videoId + ".m3u8"

  let configuration: JWConfig = JWConfig(contentURL: playerURL)
  configuration.controls = true
  configuration.autostart = true
  configuration.image = imageURL

  jwPlayer = JWPlayerController(config: configuration)
  jwPlayer?.forceFullScreenOnLandscape = true
  jwPlayer?.forceLandscapeOnFullScreen = true
  jwPlayer?.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
  jwPlayer?.view?.frame = bounds
  jwPlayer?.delegate = self
  jwPlayer?.volume = 0.0

  if let view = jwPlayer?.view {
      addSubview(view)
  }

}

2. Solution (keep the player and the view instance and reset the configuration of the player)

  private func setupPlayer() {

  guard let videoId = self.videoId else { return }

  let playerURL = jwPlayerURL + videoId + ".m3u8"

  let configuration: JWConfig = JWConfig(contentURL: playerURL)
  configuration.controls = true
  configuration.autostart = true
  configuration.image = imageURL

  if jwPlayer == nil {

      jwPlayer = JWPlayerController(config: configuration)
      jwPlayer?.forceFullScreenOnLandscape = true
      jwPlayer?.forceLandscapeOnFullScreen = true
      jwPlayer?.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
      jwPlayer?.view?.frame = bounds
      jwPlayer?.delegate = self
      jwPlayer?.volume = 0.0

      if let view = jwPlayer?.view {
          addSubview(view)
      }
  }else{
    //reset the configuration of the player here. but i dont now how this is possible with jwPlayer
  }

}

Wien answered 5/8, 2019 at 15:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.