Embed the YouTube player in a SwiftUI app. The implementation follows the current YouTube documentation: the IFrame Player API, the player parameters and the Required Minimum Functionality policy.
Note
This is not an official library. It is not affiliated with, endorsed by, or maintained by Google or YouTube. YouTube is a trademark of Google LLC. Apps that use it are subject to the YouTube API Services Terms of Service.
- iOS 17+, macOS 14+, Mac Catalyst 17+, visionOS 1+
- Swift 6.2+
tvOS and watchOS are not supported because they have no WKWebView. A sandboxed macOS app needs the Outgoing Connections (Client) capability to reach YouTube.
Add the package in Xcode via File → Add Package Dependencies, or to your Package.swift:
.package(url: "https://github.com/OpenSwiftXO/YouTubePlayer", from: "2.0.0").target(name: "YourApp", dependencies: ["YouTubePlayer"])import SwiftUI
import YouTubePlayer
struct ContentView: View {
@State private var player = YouTubePlayer()
var body: some View {
YouTubePlayerView(player: player)
.aspectRatio(16 / 9, contentMode: .fit)
.onAppear { player.load(videoID: "dQw4w9WgXcQ") }
}
}Load a list of videos or a playlist, and pass player parameters with YouTubePlayerVars:
player.load(videoIDs: ["aqz-KE-bpKQ", "jNQXAC9IVRw"])
player.load(
playlistID: "PLxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
playerVars: YouTubePlayerVars(autoplay: true, loop: true, playsInline: true)
)Show content on top of the player for each phase:
YouTubePlayerView(player: player) { phase in
switch phase {
case .loading:
ProgressView()
case .active:
EmptyView() // keep the player and its controls uncovered
case .failed(let error):
Text(error.localizedDescription)
}
}player.play()
player.pause()
player.seek(to: 120)
player.mute()
player.setPlaybackRate(1.5)
player.loadVideo(id: "aqz-KE-bpKQ", startSeconds: 10) // switch video on a loaded player
player.cueVideo(id: "aqz-KE-bpKQ") // load without playing
player.nextVideo()Commands sent while the player is starting are queued until it is ready. Commands sent before the first load are ignored.
These properties are observable, so they work with onChange(of:):
phase, isReady, state, playbackQuality, playbackRate, currentTime, duration, isAutoplayBlocked, lastError
.onChange(of: player.state) { _, state in print(state) }Other values are async queries that throw YouTubePlayerError.notReady until the player is ready:
let rates = try await player.availablePlaybackRates()
let isMuted = try await player.isMuted()YouTubePlayerError covers the IFrame API's onError codes (invalidParameter, html5Error, videoNotFound, notEmbeddable, clientIdentityMissing), plus apiFailedToLoad, notReady and unknown(code:). It conforms to LocalizedError.
YouTube requires embedded players to identify the app with an HTTP Referer of the form https://<bundle identifier>. YouTubePlayer sends it for you. Without it the player fails with error 153 (clientIdentityMissing). To use a different identifier:
@State private var player = YouTubePlayer(appIdentifier: "com.example.myapp")YouTube's policies also ask for a player viewport of at least 200×200 points (480×270 is recommended for 16:9) and that nothing covers the player or its controls.
Examples/YouTubePlayerExample is a small app for iOS, macOS and visionOS. Open YouTubePlayerExample.xcodeproj and run. The project uses Xcode 27's JSON project format, so it needs Xcode 27 or later.
Based on Google's youtube-ios-player-helper, by Ikai Lan, Ibrahim Ulukaya and Yoshifumi Yamaguchi.
Available under the Apache 2.0 license. See the LICENSE file for more info.