feat: add visionOS support (#3425)
* feat: add visionOS to target platforms * disable unsupported API * add temporary `promises` patches * fix(visionOS): update promises patches * apply code review suggestions * format code
This commit is contained in:
		@@ -118,7 +118,9 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        _playerRateChangeObserver = player.observe(\.rate, options: [.old], changeHandler: _handlers.handlePlaybackRateChange)
 | 
					        _playerRateChangeObserver = player.observe(\.rate, options: [.old], changeHandler: _handlers.handlePlaybackRateChange)
 | 
				
			||||||
        _playerVolumeChangeObserver = player.observe(\.volume, options: [.old], changeHandler: _handlers.handleVolumeChange)
 | 
					        _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() {
 | 
					    func removePlayerObservers() {
 | 
				
			||||||
@@ -151,11 +153,13 @@ class RCTPlayerObserver: NSObject, AVPlayerItemMetadataOutputPushDelegate {
 | 
				
			|||||||
    func addPlayerViewControllerObservers() {
 | 
					    func addPlayerViewControllerObservers() {
 | 
				
			||||||
        guard let playerViewController, let _handlers else { return }
 | 
					        guard let playerViewController, let _handlers else { return }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _playerViewControllerReadyForDisplayObserver = playerViewController.observe(
 | 
					        #if !os(visionOS)
 | 
				
			||||||
            \.isReadyForDisplay,
 | 
					            _playerViewControllerReadyForDisplayObserver = playerViewController.observe(
 | 
				
			||||||
            options: [.new],
 | 
					                \.isReadyForDisplay,
 | 
				
			||||||
            changeHandler: _handlers.handleReadyForDisplay
 | 
					                options: [.new],
 | 
				
			||||||
        )
 | 
					                changeHandler: _handlers.handleReadyForDisplay
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        _playerViewControllerOverlayFrameObserver = playerViewController.contentOverlayView?.observe(
 | 
					        _playerViewControllerOverlayFrameObserver = playerViewController.contentOverlayView?.observe(
 | 
				
			||||||
            \.frame,
 | 
					            \.frame,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,24 +78,21 @@ enum RCTVideoDRM {
 | 
				
			|||||||
        contentIdData: Data
 | 
					        contentIdData: Data
 | 
				
			||||||
    ) -> Promise<Data> {
 | 
					    ) -> Promise<Data> {
 | 
				
			||||||
        return Promise<Data>(on: .global()) { fulfill, reject in
 | 
					        return Promise<Data>(on: .global()) { fulfill, reject in
 | 
				
			||||||
            var spcError: NSError!
 | 
					            #if os(visionOS)
 | 
				
			||||||
            var spcData: Data?
 | 
					                // TODO: DRM is not supported yet on visionOS. See #3467
 | 
				
			||||||
            do {
 | 
					                reject(NSError(domain: "DRM is not supported yet on visionOS", code: 0, userInfo: nil))
 | 
				
			||||||
                spcData = try loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: contentIdData as Data, options: nil)
 | 
					            #else
 | 
				
			||||||
            } catch _ {
 | 
					                guard let spcData = try? loadingRequest.streamingContentKeyRequestData(
 | 
				
			||||||
                print("SPC error")
 | 
					                    forApp: certificateData,
 | 
				
			||||||
            }
 | 
					                    contentIdentifier: contentIdData as Data,
 | 
				
			||||||
 | 
					                    options: nil
 | 
				
			||||||
 | 
					                ) else {
 | 
				
			||||||
 | 
					                    reject(RCTVideoErrorHandler.noSPC)
 | 
				
			||||||
 | 
					                    return
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if spcError != nil {
 | 
					                fulfill(spcData)
 | 
				
			||||||
                reject(spcError)
 | 
					            #endif
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            guard let spcData else {
 | 
					 | 
				
			||||||
                reject(RCTVideoErrorHandler.noSPC)
 | 
					 | 
				
			||||||
                return
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            fulfill(spcData)
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,9 +28,11 @@ enum RCTVideoAssetsUtils {
 | 
				
			|||||||
                asset.loadTracks(withMediaType: withMediaType, completionHandler: handler)
 | 
					                asset.loadTracks(withMediaType: withMediaType, completionHandler: handler)
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            return Promise { fulfill, _ in
 | 
					            #if !os(visionOS)
 | 
				
			||||||
                fulfill(asset.tracks(withMediaType: withMediaType))
 | 
					                return Promise { fulfill, _ in
 | 
				
			||||||
            }
 | 
					                    fulfill(asset.tracks(withMediaType: withMediaType))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            #endif
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -508,7 +508,9 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
 | 
				
			|||||||
    @objc
 | 
					    @objc
 | 
				
			||||||
    func setAllowsExternalPlayback(_ allowsExternalPlayback: Bool) {
 | 
					    func setAllowsExternalPlayback(_ allowsExternalPlayback: Bool) {
 | 
				
			||||||
        _allowsExternalPlayback = allowsExternalPlayback
 | 
					        _allowsExternalPlayback = allowsExternalPlayback
 | 
				
			||||||
        _player?.allowsExternalPlayback = _allowsExternalPlayback
 | 
					        #if !os(visionOS)
 | 
				
			||||||
 | 
					            _player?.allowsExternalPlayback = _allowsExternalPlayback
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @objc
 | 
					    @objc
 | 
				
			||||||
@@ -642,7 +644,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
 | 
				
			|||||||
        RCTPlayerOperations.configureAudio(ignoreSilentSwitch: _ignoreSilentSwitch, mixWithOthers: _mixWithOthers, audioOutput: _audioOutput)
 | 
					        RCTPlayerOperations.configureAudio(ignoreSilentSwitch: _ignoreSilentSwitch, mixWithOthers: _mixWithOthers, audioOutput: _audioOutput)
 | 
				
			||||||
        do {
 | 
					        do {
 | 
				
			||||||
            if audioOutput == "speaker" {
 | 
					            if audioOutput == "speaker" {
 | 
				
			||||||
                #if os(iOS)
 | 
					                #if os(iOS) || os(visionOS)
 | 
				
			||||||
                    try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
 | 
					                    try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
 | 
				
			||||||
                #endif
 | 
					                #endif
 | 
				
			||||||
            } else if audioOutput == "earpiece" {
 | 
					            } else if audioOutput == "earpiece" {
 | 
				
			||||||
@@ -713,7 +715,9 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if #available(iOS 12.0, tvOS 12.0, *) {
 | 
					        if #available(iOS 12.0, tvOS 12.0, *) {
 | 
				
			||||||
            _player?.preventsDisplaySleepDuringVideoPlayback = _preventsDisplaySleepDuringVideoPlayback
 | 
					            #if !os(visionOS)
 | 
				
			||||||
 | 
					                _player?.preventsDisplaySleepDuringVideoPlayback = _preventsDisplaySleepDuringVideoPlayback
 | 
				
			||||||
 | 
					            #endif
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Fallback on earlier versions
 | 
					            // Fallback on earlier versions
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -972,12 +976,8 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let filter: CIFilter! = CIFilter(name: filterName)
 | 
					        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
 | 
				
			||||||
            RCTVideoUtils.generateVideoComposition(asset: _playerItem.asset, filter: filter).then { [weak self] composition in
 | 
					            self?._playerItem?.videoComposition = composition
 | 
				
			||||||
                self?._playerItem?.videoComposition = composition
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            // Fallback on earlier versions
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1280,9 +1280,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    func handleExternalPlaybackActiveChange(player _: AVPlayer, change _: NSKeyValueObservedChange<Bool>) {
 | 
					    func handleExternalPlaybackActiveChange(player _: AVPlayer, change _: NSKeyValueObservedChange<Bool>) {
 | 
				
			||||||
        guard let _player else { return }
 | 
					        #if !os(visionOS)
 | 
				
			||||||
        onVideoExternalPlaybackChange?(["isExternalPlaybackActive": NSNumber(value: _player.isExternalPlaybackActive),
 | 
					            guard let _player else { return }
 | 
				
			||||||
                                        "target": reactTag as Any])
 | 
					            onVideoExternalPlaybackChange?(["isExternalPlaybackActive": NSNumber(value: _player.isExternalPlaybackActive),
 | 
				
			||||||
 | 
					                                            "target": reactTag as Any])
 | 
				
			||||||
 | 
					        #endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    func handleViewControllerOverlayViewFrameChange(overlayView _: UIView, change: NSKeyValueObservedChange<CGRect>) {
 | 
					    func handleViewControllerOverlayViewFrameChange(overlayView _: UIView, change: NSKeyValueObservedChange<CGRect>) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										41
									
								
								ios/patches/PromisesObjC.podspec
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								ios/patches/PromisesObjC.podspec
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
				
			||||||
							
								
								
									
										21
									
								
								ios/patches/PromisesSwift.podspec
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								ios/patches/PromisesSwift.podspec
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
				
			||||||
@@ -9,11 +9,10 @@ Pod::Spec.new do |s|
 | 
				
			|||||||
  s.description    = package['description']
 | 
					  s.description    = package['description']
 | 
				
			||||||
  s.license        = package['license']
 | 
					  s.license        = package['license']
 | 
				
			||||||
  s.author         = package['author']
 | 
					  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.homepage       = 'https://github.com/react-native-video/react-native-video'
 | 
				
			||||||
  s.tvos.deployment_target = "10.0"
 | 
					  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|
 | 
					  s.subspec "Video" do |ss|
 | 
				
			||||||
    ss.source_files = "ios/Video/**/*.{h,m,swift}"
 | 
					    ss.source_files = "ios/Video/**/*.{h,m,swift}"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user