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:
parent
8f1bdb7c36
commit
cf3ebb7f15
@ -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}"
|
||||||
|
Loading…
Reference in New Issue
Block a user