diff --git a/ios/Video/Features/RCTPlayerObserver.swift b/ios/Video/Features/RCTPlayerObserver.swift index f20447d3..00227feb 100644 --- a/ios/Video/Features/RCTPlayerObserver.swift +++ b/ios/Video/Features/RCTPlayerObserver.swift @@ -118,7 +118,9 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate { _playerRateChangeObserver = player.observe(\.rate, options: [.old], changeHandler: _handlers.handlePlaybackRateChange) _playerVolumeChangeObserver = player.observe(\.volume, options: [.old], changeHandler: _handlers.handleVolumeChange) - _playerExternalPlaybackActiveObserver = player.observe(\.isExternalPlaybackActive, changeHandler: _handlers.handleExternalPlaybackActiveChange) + #if !os(visionOS) + _playerExternalPlaybackActiveObserver = player.observe(\.isExternalPlaybackActive, changeHandler: _handlers.handleExternalPlaybackActiveChange) + #endif } func removePlayerObservers() { @@ -151,11 +153,13 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate { func addPlayerViewControllerObservers() { guard let playerViewController, let _handlers else { return } - _playerViewControllerReadyForDisplayObserver = playerViewController.observe( - \.isReadyForDisplay, - options: [.new], - changeHandler: _handlers.handleReadyForDisplay - ) + #if !os(visionOS) + _playerViewControllerReadyForDisplayObserver = playerViewController.observe( + \.isReadyForDisplay, + options: [.new], + changeHandler: _handlers.handleReadyForDisplay + ) + #endif _playerViewControllerOverlayFrameObserver = playerViewController.contentOverlayView?.observe( \.frame, diff --git a/ios/Video/Features/RCTVideoDRM.swift b/ios/Video/Features/RCTVideoDRM.swift index 6abf27e2..cea37a1a 100644 --- a/ios/Video/Features/RCTVideoDRM.swift +++ b/ios/Video/Features/RCTVideoDRM.swift @@ -78,24 +78,21 @@ enum RCTVideoDRM { contentIdData: Data ) -> Promise { return Promise(on: .global()) { fulfill, reject in - var spcError: NSError! - var spcData: Data? - do { - spcData = try loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: contentIdData as Data, options: nil) - } catch _ { - print("SPC error") - } + #if os(visionOS) + // TODO: DRM is not supported yet on visionOS. See #3467 + reject(NSError(domain: "DRM is not supported yet on visionOS", code: 0, userInfo: nil)) + #else + guard let spcData = try? loadingRequest.streamingContentKeyRequestData( + forApp: certificateData, + contentIdentifier: contentIdData as Data, + options: nil + ) else { + reject(RCTVideoErrorHandler.noSPC) + return + } - if spcError != nil { - reject(spcError) - } - - guard let spcData else { - reject(RCTVideoErrorHandler.noSPC) - return - } - - fulfill(spcData) + fulfill(spcData) + #endif } } diff --git a/ios/Video/Features/RCTVideoUtils.swift b/ios/Video/Features/RCTVideoUtils.swift index 44e1fdc8..3a1011e6 100644 --- a/ios/Video/Features/RCTVideoUtils.swift +++ b/ios/Video/Features/RCTVideoUtils.swift @@ -28,9 +28,11 @@ enum RCTVideoAssetsUtils { asset.loadTracks(withMediaType: withMediaType, completionHandler: handler) } } else { - return Promise { fulfill, _ in - fulfill(asset.tracks(withMediaType: withMediaType)) - } + #if !os(visionOS) + return Promise { fulfill, _ in + fulfill(asset.tracks(withMediaType: withMediaType)) + } + #endif } } } diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index c16dcfcd..2fd5db9c 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -508,7 +508,9 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH @objc func setAllowsExternalPlayback(_ allowsExternalPlayback: Bool) { _allowsExternalPlayback = allowsExternalPlayback - _player?.allowsExternalPlayback = _allowsExternalPlayback + #if !os(visionOS) + _player?.allowsExternalPlayback = _allowsExternalPlayback + #endif } @objc @@ -642,7 +644,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH RCTPlayerOperations.configureAudio(ignoreSilentSwitch: _ignoreSilentSwitch, mixWithOthers: _mixWithOthers, audioOutput: _audioOutput) do { if audioOutput == "speaker" { - #if os(iOS) + #if os(iOS) || os(visionOS) try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.speaker) #endif } else if audioOutput == "earpiece" { @@ -713,7 +715,9 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } if #available(iOS 12.0, tvOS 12.0, *) { - _player?.preventsDisplaySleepDuringVideoPlayback = _preventsDisplaySleepDuringVideoPlayback + #if !os(visionOS) + _player?.preventsDisplaySleepDuringVideoPlayback = _preventsDisplaySleepDuringVideoPlayback + #endif } else { // Fallback on earlier versions } @@ -972,12 +976,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } let filter: CIFilter! = CIFilter(name: filterName) - if #available(iOS 9.0, *), let _playerItem { - RCTVideoUtils.generateVideoComposition(asset: _playerItem.asset, filter: filter).then { [weak self] composition in - self?._playerItem?.videoComposition = composition - } - } else { - // Fallback on earlier versions + RCTVideoUtils.generateVideoComposition(asset: _playerItem!.asset, filter: filter).then { [weak self] composition in + self?._playerItem?.videoComposition = composition } } @@ -1280,9 +1280,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH } func handleExternalPlaybackActiveChange(player _: AVPlayer, change _: NSKeyValueObservedChange) { - guard let _player else { return } - onVideoExternalPlaybackChange?(["isExternalPlaybackActive": NSNumber(value: _player.isExternalPlaybackActive), - "target": reactTag as Any]) + #if !os(visionOS) + guard let _player else { return } + onVideoExternalPlaybackChange?(["isExternalPlaybackActive": NSNumber(value: _player.isExternalPlaybackActive), + "target": reactTag as Any]) + #endif } func handleViewControllerOverlayViewFrameChange(overlayView _: UIView, change: NSKeyValueObservedChange) { diff --git a/ios/patches/PromisesObjC.podspec b/ios/patches/PromisesObjC.podspec new file mode 100644 index 00000000..11ece874 --- /dev/null +++ b/ios/patches/PromisesObjC.podspec @@ -0,0 +1,41 @@ +Pod::Spec.new do |s| + s.name = 'PromisesObjC' + s.version = '2.3.1.1' + s.authors = 'Google Inc.' + s.license = { :type => 'Apache-2.0', :file => 'LICENSE' } + s.homepage = 'https://github.com/google/promises' + s.source = { :git => 'https://github.com/google/promises.git', :tag => '2.3.1' } + s.summary = 'Synchronization construct for Objective-C' + s.description = <<-DESC + + Promises is a modern framework that provides a synchronization construct for + Objective-C to facilitate writing asynchronous code. + DESC + + s.platforms = { :ios => '9.0', :osx => '10.11', :tvos => '9.0', :watchos => '2.0', :visionos => '1.0' } + + s.module_name = 'FBLPromises' + s.prefix_header_file = false + s.header_dir = "./" + s.public_header_files = "Sources/#{s.module_name}/include/**/*.h" + s.private_header_files = "Sources/#{s.module_name}/include/FBLPromisePrivate.h" + s.source_files = "Sources/#{s.module_name}/**/*.{h,m}" + s.pod_target_xcconfig = { + 'DEFINES_MODULE' => 'YES' + } + + s.test_spec 'Tests' do |ts| + # Note: Omits watchOS as a workaround since XCTest is not available to watchOS for now. + # Reference: https://github.com/CocoaPods/CocoaPods/issues/8283, https://github.com/CocoaPods/CocoaPods/issues/4185. + ts.platforms = {:ios => nil, :osx => nil, :tvos => nil} + ts.source_files = "Tests/#{s.module_name}Tests/*.m", + "Sources/#{s.module_name}TestHelpers/include/#{s.module_name}TestHelpers.h" + end + s.test_spec 'PerformanceTests' do |ts| + # Note: Omits watchOS as a workaround since XCTest is not available to watchOS for now. + # Reference: https://github.com/CocoaPods/CocoaPods/issues/8283, https://github.com/CocoaPods/CocoaPods/issues/4185. + ts.platforms = {:ios => nil, :osx => nil, :tvos => nil} + ts.source_files = "Tests/#{s.module_name}PerformanceTests/*.m", + "Sources/#{s.module_name}TestHelpers/include/#{s.module_name}TestHelpers.h" + end +end \ No newline at end of file diff --git a/ios/patches/PromisesSwift.podspec b/ios/patches/PromisesSwift.podspec new file mode 100644 index 00000000..66f077cd --- /dev/null +++ b/ios/patches/PromisesSwift.podspec @@ -0,0 +1,21 @@ +Pod::Spec.new do |s| + s.name = 'PromisesSwift' + s.version = '2.3.1.1' + s.authors = 'Google Inc.' + s.license = { :type => 'Apache-2.0', :file => 'LICENSE' } + s.homepage = 'https://github.com/google/promises' + s.source = { :git => 'https://github.com/google/promises.git', :tag => '2.3.1' } + s.summary = 'Synchronization construct for Swift' + s.description = <<-DESC + + Promises is a modern framework that provides a synchronization construct for + Swift to facilitate writing asynchronous code. + DESC + + s.platforms = { :ios => '9.0', :osx => '10.11', :tvos => '9.0', :watchos => '2.0', :visionos => '1.0' } + s.swift_versions = ['5.0', '5.2'] + + s.module_name = 'Promises' + s.source_files = "Sources/#{s.module_name}/*.{swift}" + s.dependency 'PromisesObjC', "#{s.version}" +end \ No newline at end of file diff --git a/react-native-video.podspec b/react-native-video.podspec index 00988825..4ec8ffbd 100644 --- a/react-native-video.podspec +++ b/react-native-video.podspec @@ -9,11 +9,10 @@ Pod::Spec.new do |s| s.description = package['description'] s.license = package['license'] s.author = package['author'] - s.homepage = 'https://github.com/react-native-video/react-native-video' - s.source = { :git => "https://github.com/react-native-video/react-native-video.git", :tag => "v#{s.version}" } - s.ios.deployment_target = "9.0" - s.tvos.deployment_target = "10.0" + s.homepage = 'https://github.com/react-native-video/react-native-video' + s.source = { :git => "https://github.com/react-native-video/react-native-video.git", :tag => "v#{s.version}" } + s.platforms = { :ios => "9.0", :tvos => "10.0", :visionos => "1.0" } s.subspec "Video" do |ss| ss.source_files = "ios/Video/**/*.{h,m,swift}"