A Swift Package to easily play YouTube videos.
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, macOS and visionOS ๐ฑ ๐ฅ ๐
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: "2.0.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.
Tip
Please refer to the YouTubePlayerKit documentation for a complete overview.
A YouTubePlayer
can be easily displayed when using SwiftUI
by declaring a YouTubePlayerView
.
import SwiftUI
import YouTubePlayerKit
struct ContentView: View {
let youTubePlayer: YouTubePlayer = "https://youtube.com/watch?v=psL_5RIBqnY"
var body: some View {
YouTubePlayerView(self.youTubePlayer) { state in
// An optional overlay view for the current state of the player
switch state {
case .idle:
ProgressView()
case .ready:
EmptyView()
case .error(let error):
ContentUnavailableView(
"Error",
systemImage: "exclamationmark.triangle.fill",
description: Text("YouTube player couldn't be loaded: \(error)")
)
}
}
// Optionally react to specific updates such as the fullscreen state
.onReceive(
self.youTubePlayer.fullscreenStatePublisher
) { fullscreenState in
if fullscreenState.isFullscreen {
// ...
}
}
}
}
Tip
You can optionally mark the YouTubePlayer with @StateObject
or @ObservedObject
to automatically update your view whenever the source, parameters, or isLoggingEnabled change.
When using UIKit
or AppKit
you can make use of the YouTubePlayerViewController
or YouTubePlayerHostingView
.
import UIKit
import YouTubePlayerKit
let youTubePlayerViewController = YouTubePlayerViewController(
player: "https://youtube.com/watch?v=psL_5RIBqnY"
)
let youTubePlayerHostingView = YouTubePlayerHostingView(
player: "https://youtube.com/watch?v=psL_5RIBqnY"
)
// Access the player on both instances via the `.player` property
// Example: youTubePlayerViewController.player
A YouTubePlayer
is the central object in order to play a certain YouTube video and interact with the underlying YouTube iFrame API.
As seen in the previous examples you can initialize a player by using a string literal:
let youTubePlayer: YouTubePlayer = "https://youtube.com/watch?v=psL_5RIBqnY"
To take full control you can initialize a YouTubePlayer
with a YouTubePlayer.Source
, YouTubePlayer.Parameters
and a YouTubePlayer.Configuration
let youTubePlayer = YouTubePlayer(
// Possible values: .video, .videos, .playlist, .channel
source: .video(id: "psL_5RIBqnY"),
// The parameters of the player
parameters: .init(
autoPlay: true,
showControls: true,
loopEnabled: true,
startTime: .init(value: 5, unit: .minutes),
// ...
),
// The configuration of the underlying web view
configuration: .init(
fullscreenMode: .system,
allowsInlineMediaPlayback: true,
customUserAgent: "MyCustomUserAgent",
// ...
)
)
To differentiate between parameters and configuration, understand that parameters control the behavior and style of the YouTube player, while the configuration is linked to the underlying web view. You cannot modify the configuration after instantiation; however, it is possible to update the parameters, as shown below:
youTubePlayer.parameters.showControls = false
Warning
Updating the YouTubePlayer.Parameters
during runtime will cause the player to reload.
The YouTubePlayer.Source
is an enum which allows you to specify which YouTube source should be loaded/cued.
// A single video
let video: YouTubePlayer.Source = .video(id: "psL_5RIBqnY")
// Series of videos
let videos: YouTubePlayer.Source = .videos(ids: ["w87fOAG8fjk", "RXeOiIDNNek", "psL_5RIBqnY"])
// Playlist
let playlist: YouTubePlayer.Source = .playlist(id: "PLHFlHpPjgk72Si7r1kLGt1_aD3aJDu092")
// Channel
let channel: YouTubePlayer.Source = .channel(name: "GoogleDevelopers")
You can also use a URL to initialize a source.
let source: YouTubePlayer.Source? = .init(urlString: "https://youtube.com/watch?v=psL_5RIBqnY")
Note
The URL parsing logic is designed to handle most known YouTube URL formats, but there may be some variations that it doesn't cover.
A YouTubePlayer
lets you access the underlying YouTube Player iFrame API to play, pause, seek, retrieve video information and much more.
The majority of the APIs are async
and throwable
functions.
// Pauses the currently playing video
try await youTubePlayer.pause()
Tip
Please see the documentation for a full overview of the available APIs.
In case of an error, most functions throw a YouTubePlayer.APIError
.
This allows you to easily examine the reason for the error, any underlying error, and the executed JavaScript along with its response.
do {
try await youTubePlayer.setCaptions(fontSize: .small)
} catch {
print(
"Failed to set captions font size",
error.reason,
error.underlyingError,
error.javaScript,
error.javaScriptResponse
)
}
Additionally, several Publishers are available to react to changes of the player:
// Observe playback metadata
let cancellable = youTubePlayer
.playbackMetadataPublisher
.sink { playbackMetadata in
// ...
}
You can load a YouTube video thumbnail via the YouTubeVideoThumbnail
object.
// Initialize an instance of YouTubeVideoThumbnail
let videoThumbnail = YouTubeVideoThumbnail(
videoID: "psL_5RIBqnY",
// Choose between default, medium, high, standard, maximum
resolution: .high
)
// Retrieve the URL, if available
let url: URL? = videoThumbnail.url
// Retrieve the image, if available.
let image: YouTubeVideoThumbnail.Image? = try await videoThumbnail.image()
Additionally, the player allows you to easily retrieve the thumbnail url and image for the currently loaded video.
// Returns the video thumbnail URL of the currently loaded video
try await youTubePlayer.getVideoThumbnailURL()
/// Returns the video thumbnail of the currently loaded video
try await youTubePlayer.getVideoThumbnailImage(resolution: .maximum)
If you wish to gain more insights into the underlying communication of the YouTube Player iFrame JavaScript API, you can enable the logging of a player via the isLogginEnabled
parameter.
The YouTubePlayer
utilizes the unified logging system (OSLog) to log information about the player options, JavaScript events and evaluations.
// Enable or disable logging during initialization
let youTubePlayer = YouTubePlayer(
source: [
"w87fOAG8fjk",
"RXeOiIDNNek",
"psL_5RIBqnY"
],
isLoggingEnabled: true
)
// To update during runtime update the isLoggingEnabled property
youTubePlayer.isLoggingEnabled = false
// Additionally, you can retrieve an instance of the logger if logging is enabled.
let logger: Logger? = youTubePlayer.logger()
You can observe the incoming stream of YouTubePlayer.Event
from the underlying YouTube Player iFrame API through the following publisher.
let cancellable = youTubePlayer
.eventPublisher
.sink { event in
switch event.name {
case .playbackQualityChange:
break
case .autoplayBlocked:
break
default:
break
}
}
Important
The YouTubePlayerKit supports both official as well as unofficial/undocumented events. Please see the YouTubePlayer.Event.Name
enumeration for more details.
To run custom JavaScript on the YouTube player JavaScript instance:
try await youTubePlayer.evaluate(
javaScript: "\(.youTubePlayer).play()"
)
Note
The custom string interpolation of \(.youTubePlayer)
is a placeholder for the YouTube player JavaScript variable.
Alternatively, you can use the following convenience function to directly invoke a function on the YouTube player JavaScript object:
try await youTubePlayer.evaluate(
javaScript: .youTubePlayer(
functionName: "setLoop",
parameters: [
true
]
)
)
If you wish to further customize the underlying HTML you can configure the YouTubePlayer.HTMLBuilder
when initializing an instance of the player:
let youTubePlayer = YouTubePlayer(
source: .video(id: "psL_5RIBqnY"),
configuration: .init(
htmlBuilder: .init(
youTubePlayerJavaScriptVariableName: "youtubePlayer",
youTubePlayerEventCallbackURLScheme: "youtubeplayer",
youTubePlayerEventCallbackDataParameterName: "data",
youTubePlayerIframeAPISourceURL: .init(string: "https://www.youtube.com/iframe_api")!,
htmlProvider: { htmlBuilder, jsonEncodedYouTubePlayerOptions in
// TODO: Return custom HTML string
}
)
)
)