A Swift Package to easily play YouTube videos
with extended support for SwiftUI, Combine and async/await
Swift Version
Platforms
Build and Test Status
Documentation
Twitter
Mastodon
import SwiftUI import YouTubePlayerKit struct ContentView: View { var body: some View { // ο£Ώ WWDC 2019 Keynote YouTubePlayerView( "https://youtube.com/watch?v=psL_5RIBqnY" ) } }
- Play YouTube videos with just one line of code πΊ
- YouTube Terms of Service compliant implementation β
- Access to all native YouTube iFrame APIs π©βπ»π¨βπ»
- Support for SwiftUI, UIKit and AppKit π§βπ¨
- Runs on iOS and macOS π± π₯
Check out the example application to see YouTubePlayerKit in action. Simply open the Example/Example.xcodeproj and run the "Example" scheme.
To integrate using Apple's Swift Package Manager, add the following as a dependency to your Package.swift:
dependencies: [ .package(url: "https://github.com/SvenTiigi/YouTubePlayerKit.git", from: "1.6.0") ]
Or navigate to your Xcode project then select Swift Packages, click the "+" icon and search for YouTubePlayerKit.
Note
When integrating YouTubePlayerKit to a macOS or Mac Catalyst target please ensure to enable "Outgoing Connections (Client)" in the "Signing & Capabilities" sections.
When submitting an app to the App Store which includes the YouTubePlayerKit, please ensure to add a link to the YouTube API Terms of Services in the review notes.
https://developers.google.com/youtube/terms/api-services-terms-of-service
- Audio background playback is not supported as it violates the YouTube Terms of Service.
- Simultaneous playback of multiple YouTube players is not supported.
- Controlling playback of 360Β° videos is not supported on iOS and macOS.
A YouTube player can be easily rendered when using SwiftUI by declaring a YouTubePlayerView.
import SwiftUI import YouTubePlayerKit struct ContentView: View { @StateObject var youTubePlayer: YouTubePlayer = "https://youtube.com/watch?v=psL_5RIBqnY" var body: some View { YouTubePlayerView(self.youTubePlayer) { state in // Overlay ViewBuilder closure to place an overlay View // for the current `YouTubePlayer.State` switch state { case .idle: ProgressView() case .ready: EmptyView() case .error(let error): Text(verbatim: "YouTube player couldn't be loaded") } } } }
When using UIKit or AppKit you can make use of the YouTubePlayerViewController or YouTubePlayerHostingView.
import UIKit import YouTubePlayerKit // Initialize a YouTubePlayerViewController let youTubePlayerViewController = YouTubePlayerViewController( player: "https://youtube.com/watch?v=psL_5RIBqnY" ) // Example: Access the underlying iFrame API via the `YouTubePlayer` instance youTubePlayerViewController.player.showStatsForNerds() // Present YouTubePlayerViewController self.present(youTubePlayerViewController, animated: true)
If you wish to change the video at runtime simply update the source of a YouTubePlayer.
youTubePlayer.source = .video(id: "0TD96VTf0Xs")
Additionally, you can update the configuration of a YouTubePlayer to update the current configuration.
youTubePlayer.configuration = .init( autoPlay: true )
Note
Updating the YouTubePlayer.Configuration will result in a reload of the YouTubePlayer.
Since YouTubePlayer is conform to the ObservableObject protocol you can listen for changes whenever the source or configuration of a YouTubePlayer gets updated.
youTubePlayer .objectWillChange .sink { }
A YouTubePlayer is the central object which needs to be passed to every YouTubePlayerView, YouTubePlayerViewController or YouTubePlayerHostingView in order to play a certain YouTube video and interact with the underlying YouTube iFrame API.
Therefore, you can easily initialize a YouTubePlayer by using a string literal as seen in the previous examples.
let youTubePlayer: YouTubePlayer = "https://youtube.com/watch?v=psL_5RIBqnY"
A YouTubePlayer generally consist of a YouTubePlayer.Source and a YouTubePlayer.Configuration.
let youTubePlayer = YouTubePlayer( source: .video(id: "psL_5RIBqnY"), configuration: .init( autoPlay: true ) )
The YouTubePlayer.Source is a simple enum which allows you to specify which YouTube source should be loaded.
// YouTubePlayer Video Source let videoSource: YouTubePlayer.Source = .video(id: "psL_5RIBqnY") // YouTubePlayer Playlist Source let playlistSource: YouTubePlayer.Source = .playlist(id: "PLHFlHpPjgk72Si7r1kLGt1_aD3aJDu092") // YouTubePlayer Channel Source let channelSource: YouTubePlayer.Source = .channel(name: "iJustine")
Additionally, you can use a URL to initialize a YouTubePlayer.Source
let urlSource: YouTubePlayer.Source? = .url("https://youtube.com/watch?v=psL_5RIBqnY")
The YouTubePlayer.Configuration allows you to configure various parameters of the underlying YouTube iFrame player.
let configuration = YouTubePlayer.Configuration( // Define which fullscreen mode should be used (system or web) fullscreenMode: .system, // Custom action to perform when a URL gets opened openURLAction: { url in // ... }, // Enable auto play autoPlay: true, // Hide controls showControls: false, // Enable loop loopEnabled: true ) let youTubePlayer = YouTubePlayer( source: .url("https://youtube.com/watch?v=psL_5RIBqnY"), configuration: configuration )
Tip
Check out the YouTubePlayer.Configuration to get a list of all available parameters.
Additionally, a YouTubePlayer allows you to access the underlying YouTube player iFrame API in order to play, pause, seek or retrieve information like the current playback quality or title of the video that is currently playing.
All asynchronous functions on a YouTubePlayer can be invoked by either supplying a completion closure or by using async/await.
// Async/Await: Retrieve the current PlaybackMetadata let playbackMetadata = try await youTubePlayer.getPlaybackMetadata() // Completion-Closure: Retrieve the current PlaybackMetadata youTubePlayer.getPlaybackMetadata { result in switch result { case .success(let playbackMetadata): print(playbackMetadata) case .failure(let error): print(error) } }
// Play video youTubePlayer.play() // Pause video youTubePlayer.pause() // Stop video youTubePlayer.stop() // Seek to 60 seconds youTubePlayer.seek(to: 60, allowSeekAhead: false) // Closes any current picture-in-picture video and fullscreen video await youTubePlayer.closeAllMediaPresentations()
// A Publisher that emits the current YouTubePlayer State youTubePlayer.statePublisher // A Publisher that emits the current YouTubePlayer PlaybackState youTubePlayer.playbackStatePublisher // A Publisher that emits the current YouTubePlayer PlaybackQuality youTubePlayer.playbackQualityPublisher // A Publisher that emits the current YouTubePlayer PlaybackRate youTubePlayer.playbackRatePublisher
// Retrieve a number between 0 and 1 that specifies the percentage of the video that the player shows as buffered try await youTubePlayer.getVideoLoadedFraction() // A Publisher that emits a number between 0 and 1 that specifies the percentage of the video that the player shows as buffered youTubePlayer.videoLoadedFractionPublisher() // Retrieve the PlaybackState of the player video try await youTubePlayer.getPlaybackState() // Retrieve the elapsed time in seconds since the video started playing try await youTubePlayer.getCurrentTime() /// A Publisher that emits the current elapsed time in seconds since the video started playing youTubePlayer.currentTimePublisher() // Retrieve the current PlaybackMetadata try await youTubePlayer.getPlaybackMetadata() // A Publisher that emits the current PlaybackMetadata youTubePlayer.playbackMetadataPublisher
// Load a new video from source youTubePlayer.load(source: .url("https://youtube.com/watch?v=psL_5RIBqnY")) // Cue a video from source youTubePlayer.cue(source: .url("https://youtube.com/watch?v=psL_5RIBqnY"))
// Update the YouTubePlayer Configuration youTubePlayer.update( configuration: .init( showControls: false ) )
Note
Updating the YouTubePlayer.Configuration will result in a reload of the entire YouTubePlayer
// Reloads the player youTubePlayer.reload()
// Mutes the player youTubePlayer.mute() // Unmutes the player youTubePlayer.unmute() // Retrieve Bool value if the player is muted try await youTubePlayer.isMuted() // Retrieve the player's current volume, an integer between 0 and 100 try await youTubePlayer.getVolume()
// Show Stats for Nerds which displays additional video information youTubePlayer.showStatsForNerds() // Hide Stats for Nerds youTubePlayer.hideStatsForNerds() // Retrieve the YouTubePlayer Information try await youTubePlayer.getInformation() // Retrieve the duration in seconds of the currently playing video try await youTubePlayer.getDuration() // A Publisher that emits the duration in seconds of the currently playing video youTubePlayer.durationPublisher // Retrieve the YouTube.com URL for the currently loaded/playing video try await youTubePlayer.getVideoURL() // Retrieve the embed code for the currently loaded/playing video try await youTubePlayer.getVideoEmbedCode()
// This function loads and plays the next video in the playlist youTubePlayer.nextVideo() // This function loads and plays the previous video in the playlist youTubePlayer.previousVideo() // This function loads and plays the specified video in the playlist youTubePlayer.playVideo(at: 3) // This function indicates whether the video player should continuously play a playlist youTubePlayer.setLoop(enabled: true) // This function indicates whether a playlist's videos should be shuffled youTubePlayer.setShuffle(enabled: true) // This function returns an array of the video IDs in the playlist as they are currently ordered try await youTubePlayer.getPlaylist() // This function returns the index of the playlist video that is currently playing try await youTubePlayer.getPlaylistIndex()
// This function retrieves the playback rate of the currently playing video try await youTubePlayer.getPlaybackRate() // This function sets the suggested playback rate for the current video youTubePlayer.set(playbackRate: 1.5) // This function returns the set of playback rates in which the current video is available try await youTubePlayer.getAvailablePlaybackRates()