0

I am new to swift and mobile development. I am writing a React Native app for tvOS and I need to implement the JWPlayerTVKit SDK in swift, as the react-native-jw-media-player does not support tvOS and I already have built the react native iOS version of it.

I have managed to have the video play, however it feels like the process of handling the player stops, while the video keeps playing. Basically, the player should have controls (play, pause, time scroll, etc.) which do not appear and the event jwplayerIsReady doesn't get fired.

I am assuming that the thread that interacts with the video player dies when I have added a 20s delay after the player is configured. For 20seconds the player controls are visible and the player events respond as expected. After 20seconds, the video keeps playing, with sound, but the controls become unavailable.

Here is my code:

bridging-header.h

#import "React/RCTBridgeModule.h"
#import "React/RCTViewManager.h"

JWPlayerViewManager.m

#import "React/RCTViewManager.h"
@interface RCT_EXTERN_MODULE(JWPlayerViewManager, RCTViewManager)
@end

JWPlayerViewManager.swift

import Foundation
import React

@objc(JWPlayerViewManager)
class JWPlayerViewManager: RCTViewManager {
  override func view() -> UIView! {
      let viewController = JWPlayerViewController()
      let containerView = UIViewController()
      containerView.addChild(viewController)
      containerView.view.addSubview(viewController.view)
      return viewController.view
  }
  
  override static func requiresMainQueueSetup() -> Bool {
    return true
  }
}

JWPlayerViewController.swift

import UIKit
import JWPlayerTVKit

class JWPlayerViewController: JWCinematicViewController {
  override func viewDidLoad() {
    super.viewDidLoad()
    do {
      JWPlayerKitLicense.setLicenseKey("123123123");
    
      // Last, configure the player
      player.configurePlayer(with: try setUpPlayer())

      // I have managed to see the controls and the ready, visible events to fire after I have added this piece of code
      // note: without self.player.play() the controls and the events do not work either
      DispatchQueue.main.asyncAfter(deadline: .now() + 20) {
        self.player.play()
        print("play")
      }
    } catch {
        // Handle builder failure
        print(error)
    }
  }
  
  /**
   Set up the player with a simple configuration.
   */
  private func setUpPlayer() throws -> JWPlayerConfiguration {
          let videoSource1 = try JWVideoSourceBuilder()
            .file(URL(string:"https://cdn.jwplayer.com/videos/123.mp4")!)
            .label("270p")
            .defaultVideo(false)
            .build()
          
          let videoSource2 = try JWVideoSourceBuilder()
            .file(URL(string:"https://cdn.jwplayer.com/videos/456.mp4")!)
            .label("720p")
            .defaultVideo(true)
            .build()
          
          let mediaTrack = try JWCaptionTrackBuilder()
            .file(URL(string:"https://cdn.jwplayer.com/tracks/123.srt")!)
            .label("eng")
            .defaultTrack(true)
            .build()
          
          // First, build a player item with the stream URL
          let item = try JWPlayerItemBuilder()
              .title("My Title")
              .description("My description.")
              .videoSources([videoSource1, videoSource2])
              .mediaTracks([mediaTrack])
              .build()

          // Second, build a player configuration using the player item
          return try JWPlayerConfigurationBuilder()
              .playlist([item])
              .autostart(false)
              .build()
    }
  
  override func controlBarVisibilityChanged(isVisible: Bool) {
    print("control bar")
    print((isVisible) ? "visible" : "not visible")
  }
  
  override func jwplayer(_ player: JWPlayer, isVisible: Bool) {
      print("isVisible")
  }
  
  // Player is ready
  override func jwplayerIsReady(_ player: JWPlayer) {
      super.jwplayerIsReady(player)
    print("player is ready")
  }

App.tsx

import { Dimensions, requireNativeComponent, View } from 'react-native';

const JWPlayerView = requireNativeComponent('JWPlayerView', null);

const App = () => {
  return (
    <View style={{ flex: 1 }}>
      <JWPlayerView
        style={{
          flex: 1,
          backgroundColor: '#ff0000',
          height: Dimensions.get('window').width,
        }}
      />
    </View>
  );
};

export default App;

Please any advice is very welcomed. I am coming from a web development background, so perhaps I am misunderstanding some principles here. Thank you very much for your help!

Grig Dodon
  • 313
  • 1
  • 3
  • 8

1 Answers1

0

There are a few things that caught my attention.

First, I don't see how (or where) you use the JWPlayerViewController.swift. In the App.tsx I only see the use of a JWPlayerView. To be able to do more debugging, I'd need to see how the view controller is presented.

Second thing that caught my attention was the view controller containment:

override func view() -> UIView! {
      let viewController = JWPlayerViewController()
      let containerView = UIViewController()
      containerView.addChild(viewController)
      containerView.view.addSubview(viewController.view)
      return viewController.view
}

In the method above, you add the viewController as a child view controller of the containerView. In Apple world we call this 'view controller containment'. This might or might not fix your issue, but this is how you do view controller containment properly in Apple world:

override func view() -> UIView! {
      let playerViewController = JWPlayerViewController()
      let containerViewController = UIViewController()
      containerViewController.addChild(playerViewController)
      playerViewController.view.frame = containerViewController.frame
      containerView.view.addSubview(playerViewController.view)
      playerViewController.didMove(toParent: containerV)
      return playerViewController.view
}

I renamed the containerView to containerViewController, so we don't misinterpret it as a view. I'm wondering what's the goal of putting the playerViewController in a container view?

Third thing: I'd recommend to always call the super method in the methods you overriden in JWPlayerViewController.swift.

Please let me know if any of the recommendations fixed your issue, if not - we can look further into this.

Vladimir
  • 521
  • 5
  • 16