[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6XCYRHQ60DC0YXWRCASET1X":3,"skill-same:01M6XCYRHQ60DC0YXWRCASET1X":64},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":43,"commitSha":44,"skillDirectory":45,"forksCount":14,"repositoryHtmlUrl":46,"repositoryHomepageUrl":6,"ownerHtmlUrl":47,"ownerBlogUrl":48,"ownerDescription":6,"updatedAt":49,"install":50,"resources":63},"01M6XCYRHQ60DC0YXWRCASET1X","bitmovin-player-ios",null,"Integrate and troubleshoot the Bitmovin Player SDK for iOS, tvOS, and visionOS in Swift, SwiftUI, UIKit, and existing Objective-C apps, including playback setup, player lifecycle, source loading, DRM, ads, AirPlay, Google Cast \u002F Chromecast, PiP, platform-specific UX, logs, network diagnostics, and stream validation.","bitmovin\u002Fskills","bitmovin","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F3273006?v=4","01M6XCX24ZDCBTPHWAAM18VJQY","Bitmovin","01M6XCX29H5E446MM0E5XAVQTC",1,"MIT","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-player-ios\u002FSKILL.md",[],[9,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"bitmovin player ios","ios","player","platform","objective","stream","Existing","for","pip","apps","google","integrate","uikit","swiftui","ads","lifecycle","logs","validation","swift","2026-11-10 10:12:30",false,"---\nname: bitmovin-player-ios\ndescription: Integrate and troubleshoot the Bitmovin Player SDK for iOS, tvOS, and visionOS in Swift, SwiftUI, UIKit, and existing Objective-C apps, including playback setup, player lifecycle, source loading, DRM, ads, AirPlay, Google Cast \u002F Chromecast, PiP, platform-specific UX, logs, network diagnostics, and stream validation.\n---\n\n# Bitmovin Player Apple SDK Integration\n\n## API source of truth\n\nUse the versioned Bitmovin Player Apple SDK markdown manifest as the authoritative reference for API behavior, class names, method signatures, setup steps, availability, and feature integration:\n\n```text\nhttps:\u002F\u002Fcdn.bitmovin.com\u002Fplayer\u002Fios\u002F3\u002FBitmovinPlayer-markdown-manifest.json\n```\n\nVersion policy: if the app already uses a specific Bitmovin Player SDK version, prefer the matching versioned manifest when available. If the user has not specified a version or the matching manifest is unavailable, use the pinned manifest above until a newer source of truth is provided.\n\nLookup workflow:\n\n1. Fetch the manifest.\n2. Search `documents[]` by `title` or `identifier` for the API symbol\u002Farticle.\n3. Take the matching `identifier`, lowercase the full path, prefix it with `\u002Fdata`, and append `.md`.\n4. Fetch that markdown file from the same versioned CDN base.\n\nExample:\n\n```text\nidentifier: \u002Fdocumentation\u002FBitmovinPlayerCore\u002FPlayerEventsApi\nmarkdown:   https:\u002F\u002Fcdn.bitmovin.com\u002Fplayer\u002Fios\u002F3\u002Fdata\u002Fdocumentation\u002Fbitmovinplayercore\u002Fplayereventsapi.md\n```\n\nIf a markdown file is unavailable, fall back to the corresponding DocC JSON at the same `\u002Fdata\u002F...json` path and state that the answer is derived from DocC JSON rather than markdown.\n\nUse the samples repository as the implementation companion reference for wiring examples and end-to-end app setup:\n\n```text\nhttps:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fbitmovin-player-ios-samples\n```\n\nUse iOS\u002FtvOS\u002FvisionOS release notes to determine available versions, recent changes, migration notes, and the latest Bitmovin Player Apple SDK version:\n\n```text\nhttps:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Frelease-notes-ios.md\n```\n\nIf examples, local source, cached snippets, or prior assumptions conflict with the manifest-backed markdown docs, follow the markdown docs.\n\n## When to use this skill\n\nUse this skill when the task involves Bitmovin Player SDK integration or troubleshooting in an Apple-platform app:\n\n- Add or upgrade Bitmovin Player SDK in an iOS, tvOS, or visionOS app\n- Wire playback into SwiftUI, UIKit, or mixed Swift\u002FObjective-C code\n- Fix compile\u002Fruntime issues in player setup, source loading, events, ads, DRM, or analytics\n- Diagnose unexpected playback behavior, stalls, startup failures, stream issues, or network failures\n- Add or debug AirPlay, Google Cast \u002F Chromecast, Picture in Picture, fullscreen, background playback, or remote-control behavior\n- Adapt one playback pipeline across iPhone, iPad, Apple TV, and visionOS surfaces\n- Turn session-specific integration roadblocks into concise guidance that helps the agent reach a working integration faster\n\n## Supported deployment targets\n\nUnless the user or project explicitly requires a higher baseline, preserve these minimum deployment versions:\n\n- iOS 14+\n- tvOS 14+\n- visionOS 1+\n\n## Language policy\n\n- Prefer Swift for new integrations.\n- SwiftUI and UIKit are both valid depending on the host app.\n- Objective-C is supported for existing codebases and interoperability, but discourage starting a new Bitmovin Player integration in Objective-C unless the user explicitly asks or the project is already Objective-C-first.\n\n## Dependency installation\n\nUse Swift Package Manager for new integrations.\n\n### Swift Package Manager\n\nIn Xcode, add the Bitmovin Player SDK through Project > Package Dependencies with this package URL:\n\n```text\nhttps:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fplayer-ios.git\n```\n\nFor `Package.swift` based projects, add the package dependency and use the `BitmovinPlayer` product:\n\n```swift\ndependencies: [\n    .package(url: \"https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fplayer-ios.git\", exact: \"Version Number\")\n],\ntargets: [\n    .target(\n        name: \"\u003CNAME_OF_YOUR_PACKAGE>\",\n        dependencies: [\n            .product(name: \"BitmovinPlayer\", package: \"player-ios\")\n        ]\n    )\n]\n```\n\nReplace `Version Number` with the desired SDK version. If a user has not requested a specific version, verify the current recommended\u002Flatest version before choosing one.\n\nNote: command-line `swift build` is currently not supported for packages depending on `BitmovinPlayer`; open the package in Xcode instead.\n\n### CocoaPods\n\nCocoaPods is deprecated for this SDK and discouraged for new integrations. Only keep CocoaPods when maintaining an existing Podfile-based integration and the user explicitly wants to avoid migration. Prefer moving new work to Swift Package Manager.\n\n### Analytics \u002F Observability collector versioning\n\nThe analytics \u002F observability collector version can be updated independently from the player version. Do not force a Bitmovin Player SDK upgrade only to update the collector, as long as the selected collector satisfies the minimum version required by the installed player version.\n\n## Core principles\n\n1. **Verify APIs before editing**\n   - Resolve SDK-specific APIs through the API source-of-truth section before editing code.\n   - Before writing SDK-specific code, inspect the app's installed SDK version, dependency manager, generated symbols, local examples, and the matching manifest-backed markdown docs.\n   - When a requested feature is unfamiliar or the integration path is unclear, first search the matching API docs\u002Fmanifest for relevant symbols or articles. If docs are missing or ambiguous, inspect local generated headers\u002Fpackage symbols before writing code, and state the evidence used.\n   - Do not invent class names, initializers, config fields, or event names.\n\n2. **Use one stable playback pipeline**\n   - Keep player creation, source loading, event subscription, and teardown centralized.\n   - Vary platform UI around that core pipeline instead of forking playback logic per device class.\n\n3. **Respect Apple lifecycle boundaries**\n   - Keep player instances stable for a playback scope.\n   - Rebind views or controllers for UI changes instead of recreating the player during normal layout, navigation, fullscreen, or SwiftUI state changes.\n   - Destroy\u002Frelease player resources only when the playback context is finished.\n\n4. **Do not bypass the SDK**\n   - Never access or depend on the backing `AVPlayer` instance from the Bitmovin `Player` instance.\n   - Use documented Bitmovin Player APIs for playback state, control, events, source handling, and feature integration.\n   - If a use case appears to require direct `AVPlayer` access, treat that as a design roadblock and look for an SDK-supported API or escalate the missing capability to the user.\n\n5. **Start troubleshooting from evidence**\n   - Enable verbose\u002Fdebug logging before reproducing issues.\n   - Check SDK logs, app playback logs, event order, network requests, and stream validation output before changing code.\n   - Separate player integration bugs from stream authoring, license, DRM, network, and platform-lifecycle issues.\n\n6. **Derive roadblock-lifting guidance**\n   - When integration stalls, identify the exact roadblock: missing API knowledge, project wiring, platform entitlement, network visibility, stream validity, lifecycle timing, or test coverage.\n   - Convert verified findings into a compact task-local \"skill fragment\": what was checked, what was learned, the command or code path used, and what to avoid next.\n   - Do not persist new durable memory or skill files unless the user explicitly asks.\n\n## Integration workflow\n\n### 1) Gather project context first\n\nBefore code changes, inspect:\n\n- Target platforms: iOS, tvOS, visionOS, or shared Apple-platform module\n- Deployment targets, preserving iOS 14+, tvOS 14+, and visionOS 1+ unless the project already requires higher\n- UI stack: SwiftUI, UIKit, AppKit bridge, or mixed approach\n- Language boundary: Swift-first for new work; Objective-C only for existing Objective-C codebases or interoperability\n- Dependency manager: prefer Swift Package Manager; treat CocoaPods as legacy maintenance only\n- Installed Bitmovin Player SDK version and related modules\n- Analytics \u002F observability collector version, which can usually move independently as long as it satisfies the player's minimum collector requirement\n- Existing player wrapper\u002Fservice, DI boundary, event bus, analytics layer, or sample app pattern\n- License-key handling and bundle identifiers used by each target\n- Required features: VOD\u002Flive, DRM\u002FFairPlay, ads, analytics, subtitles, AirPlay, Google Cast, PiP, background playback, or platform-specific controls\n\n### 2) Validate dependency and target setup\n\n- Confirm each app target links the required Bitmovin SDK modules.\n- Keep SDK modules version-aligned.\n- Treat the analytics \u002F observability collector as independently updatable when it still satisfies the player SDK's minimum required collector version.\n- For new integrations, add `https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fplayer-ios.git` through Swift Package Manager and depend on the `BitmovinPlayer` product.\n- Avoid introducing CocoaPods; it is deprecated and should only remain for existing Podfile-based integrations when migration is out of scope.\n- Confirm deployment targets remain at or above the supported minimums: iOS 14, tvOS 14, visionOS 1.\n- Verify target platform support before wiring a feature into tvOS or visionOS.\n- Check app capabilities and entitlements when a feature depends on platform support.\n- Avoid broad project-file churn; update only the targets and package configuration required for the task.\n\n### 3) Build player construction deliberately\n\n- Create player\u002Fconfig objects in one clear owner, such as a playback service, view model, coordinator, or controller.\n- Keep license, playback, advertising, DRM, analytics, and style configuration explicit.\n- Avoid scattering one-off config mutation across SwiftUI views or UIKit lifecycle callbacks.\n- Treat config objects as construct-and-apply inputs unless current SDK docs explicitly recommend mutation.\n\n### 4) Attach UI without destabilizing playback\n\n- SwiftUI: use a stable representable\u002Fcoordinator boundary for SDK views or controllers.\n- UIKit: keep view-controller lifecycle, player ownership, and teardown explicit.\n- tvOS: preserve focus and remote-control behavior; do not assume touch-first controls map cleanly.\n- visionOS: confirm feature availability and layout expectations before reusing iOS-specific UI behavior.\n- Fullscreen\u002FPiP\u002FAirPlay should integrate with the SDK and platform lifecycle instead of causing player recreation.\n\n#### SwiftUI fullscreen gotcha\n\nThe SwiftUI `VideoPlayerView` wrapper is suitable for inline playback, but SDK fullscreen is not supported through that SwiftUI wrapper yet because it does not expose the underlying `PlayerView` \u002F `fullscreenHandler` surface.\n\nIf a SwiftUI app needs Bitmovin SDK fullscreen, wrap UIKit `PlayerView` in `UIViewRepresentable`, keep one stable `PlayerView` instance in the coordinator, set `playerView.fullscreenHandler`, and reparent that same view into a full-screen `UIViewController` on enter\u002Fexit. Do not create a second `Player` or replace the playback pipeline for fullscreen.\n\nFor fullscreen orientation behavior, check the app's supported interface orientations and make the presented fullscreen controller participate in rotation explicitly when needed.\n\n#### PiP and background playback recipe\n\nFor iOS Picture in Picture and background playback, wire all required layers together instead of setting only the SDK flag:\n\n- Enable background playback on the player config, for example `playerConfig.playbackConfig.isBackgroundPlaybackEnabled = true`.\n- Enable PiP on the player view config, for example `playerViewConfig.pictureInPictureConfig.isEnabled = true`.\n- If the product should enter PiP when backgrounded, verify and set the current SDK's auto-enter property, for example `playerViewConfig.pictureInPictureConfig.shouldEnterOnBackground = true`.\n- Configure `AVAudioSession` with a playback category such as `.playback` before playback starts.\n- Add `UIBackgroundModes` with `audio` to the app's Info.plist or target configuration.\n- After building, inspect the built app's Info.plist to confirm `UIBackgroundModes` is actually present as an array containing `audio`; generated Info.plist build settings may not preserve this correctly in every project setup.\n\n### 5) Add Google Cast deliberately\n\nGoogle Cast support is not just an SDK flag. Wire all three layers: Bitmovin Cast API, Google Cast sender SDK dependency, and iOS local-network discovery configuration.\n\nBefore editing:\n\n- Confirm the installed Bitmovin Player SDK version from `Package.resolved`, the project file, or the local package checkout.\n- Verify the current Cast API from manifest-backed docs or generated headers. For Bitmovin Player 3.113.x, the relevant surface is `BitmovinCastManager.initializeCasting(...)`, `PlayerConfig.remoteControlConfig.isCastEnabled`, and, for custom controls, `player.castVideo()` \u002F `player.castStop()`.\n- Check whether the project already has Google Cast linked through CocoaPods, a vendored framework, or a Swift package. Do not assume the `BitmovinPlayer` SPM product includes the Google Cast sender SDK.\n- If the user points to a local sample, compare that sample's package, Info.plist, app startup, and project-file wiring before inventing a new dependency shape.\n\nFor an SPM-based setup with the Google Cast binary SDK:\n\n1. Add a local Swift package such as `GoogleCastSPMProxy\u002FPackage.swift` or mirror the user's existing sample path. The package should expose a product named `GoogleCast` backed by a binary target named `GoogleCast`.\n2. Use Google's Cast SDK ZIP URL and checksum for the selected version. A previously validated local setup used `GoogleCastSDK-ios-4.8.4_dynamic.zip` with checksum `c9c3a794e8585198b59c6bb7da5418a3194ffa1ffa6f9a1cbdf4dc0ea26dc6cf`, but verify the current URL\u002Fchecksum before choosing a new version.\n3. Add the local package reference to the Xcode project and link the `GoogleCast` product into every app target that enables casting. Prefer Xcode\u002Fpackage tooling when available; if manually patching `project.pbxproj`, follow the file's existing `XCLocalSwiftPackageReference`, `XCSwiftPackageProductDependency`, `PBXBuildFile`, `packageReferences`, and `packageProductDependencies` patterns exactly.\n4. Ensure the proxy is a normal tracked folder, not a nested git repository or gitlink. If GitHub shows `Subproject commit ...`, run `git ls-files --stage \u003Cproxy-path> .gitmodules`; mode `160000` means SwiftPM files inside the folder will not be visible as normal repo files.\n\nTypical app-side wiring:\n\n```swift\nimport BitmovinPlayer\nimport SwiftUI\n\n@main\nstruct PlaybackApp: App {\n    init() {\n        BitmovinCastManager.initializeCasting()\n    }\n\n    var body: some Scene {\n        WindowGroup {\n            ContentView()\n        }\n    }\n}\n```\n\nEnable casting where the player config is created, keeping it near the existing playback configuration:\n\n```swift\nlet playerConfig = PlayerConfig()\nplayerConfig.key = licenseKey\nplayerConfig.remoteControlConfig.isCastEnabled = true\nlet player = PlayerFactory.createPlayer(playerConfig: playerConfig)\n```\n\nFor iOS 14+ local-network discovery, add Info.plist entries for Google Cast Bonjour services. For the default Bitmovin Cast receiver, include both the generic service and the receiver-specific service:\n\n```xml\n\u003Ckey>NSBonjourServices\u003C\u002Fkey>\n\u003Carray>\n    \u003Cstring>_googlecast._tcp\u003C\u002Fstring>\n    \u003Cstring>_FFE417E5._googlecast._tcp\u003C\u002Fstring>\n\u003C\u002Farray>\n\u003Ckey>NSLocalNetworkUsageDescription\u003C\u002Fkey>\n\u003Cstring>This app uses the local network to discover Google Cast devices.\u003C\u002Fstring>\n```\n\nIf the app uses a custom receiver app id, replace `FFE417E5` with that app id in the receiver-specific Bonjour entry after verifying the Bitmovin Cast initialization API for custom receivers.\n\nAfter wiring:\n\n- Run `plutil -lint` on edited plist files.\n- Build with Xcode\u002FXcode MCP so Swift package resolution, local binary artifacts, and target product linking are checked together.\n- Inspect `Package.resolved`: local packages may not add pins, so absence of a Google Cast pin is not automatically a failure.\n- Runtime validation still requires a discoverable Cast receiver on the same network; a successful build only proves the dependency and API wiring.\n\n### 6) Load sources explicitly\n\n- Use explicit source types when the stream format is known.\n- Keep source URL, stream type, DRM, ads, subtitle, thumbnail, and analytics metadata together.\n- For source switching, prefer a deliberate transition strategy instead of destroying\u002Frecreating the player for routine changes.\n- Add deterministic fallback only when the product needs multiple playback candidates.\n\n### 7) Add advertising deliberately\n\nThe Apple SDK supports two client-side advertising module paths:\n\n- BAM \u002F Bitmovin advertising: use Bitmovin ad sources, for example `AdSource(..., ofType: .bitmovin)`. Prefer this path unless the user explicitly asks for IMA or the existing app already uses IMA.\n- IMA advertising: use IMA ad sources, for example `AdSource(..., ofType: .ima)`. This requires the Google IMA SDK to be linked by the app target in addition to the Bitmovin Player SDK; do not assume the `BitmovinPlayer` SPM product alone provides Google IMA.\n\nBefore adding ads, identify the ad module, the ad tag type (VAST, VMAP, or another supported setup), and whether existing catalog metadata is placement-only. Fields such as `ad_markers_json` can describe where ads should appear, but they are not themselves VAST\u002FVMAP ad supply unless the referenced payload contains an actual ad tag\u002Fconfig.\n\nFor a BAM pre-roll, keep the setup close to player construction:\n\n```swift\nlet adSource = AdSource(tag: vastOrVmapTagUrl, ofType: .bitmovin)\nlet preRoll = AdItem(adSources: [adSource], atPosition: \"pre\")\nplayerConfig.advertisingConfig = AdvertisingConfig(schedule: [preRoll])\n```\n\nFor IMA, first verify the current Google IMA installation instructions and the exact package\u002Fproduct or framework used by the project. Then add the Google IMA dependency to the same app target that links `BitmovinPlayer`, use `.ima` ad sources, and build the target after package resolution. If the user did not ask for IMA, do not add Google IMA.\n\nTreat public sample VAST tags as placeholders only. Prefer product-owned ad tags\u002Fconfiguration when available, and make placeholder tags obvious in code or configuration.\n\n### 8) Listen to events with Combine\n\nPrefer the modern Combine-based event API for Swift integrations:\n\nSwift files that use `ObservableObject`, `@Published`, `AnyCancellable`, or `player.events.on(...)` publishers need `import Combine` explicitly. Do not assume `SwiftUI` or `Foundation` imports make those symbols available in every file.\n\n```swift\nimport Combine\n\nfinal class PlaybackObserver {\n    private var cancellables = Set\u003CAnyCancellable>()\n\n    func bind(to player: Player) {\n        player.events.on(ReadyEvent.self)\n            .sink { event in\n                \u002F\u002F Handle player readiness.\n            }\n            .store(in: &cancellables)\n\n        player.events.on(PlayerErrorEvent.self)\n            .sink { event in\n                \u002F\u002F Handle player errors.\n            }\n            .store(in: &cancellables)\n\n        player.events.on(SourceEvent.self)\n            .sink { event in\n                \u002F\u002F Handle events emitted by the current source through the player.\n            }\n            .store(in: &cancellables)\n    }\n}\n```\n\nSwiftUI can subscribe directly with `onReceive` when the subscription lifetime matches the view:\n\n```swift\nVideoPlayerView(player: player, playerViewConfig: playerViewConfig)\n    .onReceive(player.events.on(PlayerEvent.self)) { event in\n        \u002F\u002F Observe all player events.\n    }\n    .onReceive(player.events.on(SourceEvent.self)) { event in\n        \u002F\u002F Observe source events emitted through the player.\n    }\n```\n\nEvent API guidance:\n\n- Use `player.events.on(SomePlayerEvent.self)` for player events.\n- To receive all player-session events, subscribe to both `player.events.on(PlayerEvent.self)` and `player.events.on(SourceEvent.self)`.\n- Use `player.events.on(SourceEvent.self)` for events from the current source as seen through the player.\n- Use `source.events.on(SomeSourceEvent.self)` when observing a specific `Source` instance directly.\n- Use `playerView.events.on(SomePlayerViewEvent.self)` for Swift-only `PlayerView` UI events.\n- Store `AnyCancellable`s for as long as observation should remain active.\n- Keep event handling lightweight; dispatch heavier analysis or UI state updates through the app's normal model\u002Fcoordinator boundary.\n- Never use events as a reason to reach into the backing `AVPlayer`.\n\nLegacy and Objective-C guidance:\n\n- The Combine event APIs are Swift-only.\n- Existing Objective-C integrations and older Swift code can use listener protocols such as `PlayerListener`, `SourceListener`, and `UserInterfaceListener` through `add(listener:)` \u002F `remove(listener:)`.\n- For new Swift integrations, prefer Combine publishers instead of adding listener classes.\n- If listener protocols are used, remove listeners explicitly at the same lifecycle boundary where they were added.\n\n### 9) Instrument the session\n\nWhen diagnosing or integrating a non-trivial flow:\n\n- Enable SDK verbose\u002Fdebug logging before creating player\u002Fsource objects.\n- Add app-level logs around player creation, source loading, playback start, errors, retries, ad\u002FDRM setup, lifecycle transitions, and teardown.\n- Subscribe to the relevant Combine event publishers and log event order with timestamps.\n- Include source URL, source type, SDK version, target platform, and feature flags in bug reports when safe to share.\n\n### 10) Apply the roadblock loop\n\nIf the agent is stuck:\n\n1. Name the current blocker in one sentence.\n2. State what evidence exists and what is still missing.\n3. Choose the smallest next probe: search the matching API docs for unfamiliar features, inspect SDK symbols, check logs, reproduce with a known stream, inspect network, validate the HLS stream, or isolate lifecycle behavior.\n4. After the probe, write down the verified rule that would have avoided the block.\n5. Continue integration using that rule; do not expand scope unless the evidence requires it.\n\n## Troubleshooting runbook\n\n### Always check logs first\n\n- Ensure SDK verbose\u002Fdebug logging is enabled for the reproduction.\n- Collect Xcode console logs, device logs, and app playback logs.\n- Look first for license rejection, source-load failures, manifest parsing errors, DRM\u002FFairPlay failures, ad errors, network status codes, ATS issues, and lifecycle-related teardown\u002Freload ordering.\n- If logs are noisy, add a short app-specific playback\u002Fsession prefix and filter around the reproduction window.\n\n### Inspect network requests\n\n- Suggest setting up Charles Proxy to the user when network behavior is unclear.\n- Use it to inspect manifest, segment, subtitle, DRM certificate\u002Flicense, ad tag, analytics, and retry\u002Ffallback requests.\n- Confirm HTTP status codes, redirects, headers, CORS-like service behavior, TLS\u002FATS compatibility, and response timing.\n- Treat network traces as sensitive; do not ask the user to share tokens, license keys, or private URLs unless they can redact them.\n\n### Validate HLS streams with Apple tools\n\nFor unexpected HLS playback behavior, run `mediastreamvalidator` against the sample stream when it is installed:\n\n```shell\nmediastreamvalidator \"https:\u002F\u002Fexample.com\u002Fpath\u002Fto\u002Fmaster.m3u8\"\n```\n\n- If the tool is missing, tell the user and continue with available evidence.\n- Flag validator warnings\u002Ferrors to the user instead of silently treating them as SDK bugs.\n- Distinguish stream-authoring problems from player-integration problems before proposing code changes.\n\n### Reduce to a known-good comparison\n\n- Reproduce with a known-good Bitmovin or Apple sample stream when the user's stream may be invalid.\n- Reproduce the user's stream in a minimal sample when the app integration may be the issue.\n- Compare event order, network requests, and validator output between the known-good stream and the failing stream.\n\n## Common pitfalls to avoid\n\n- Guessing APIs without checking the manifest-backed markdown docs\n- Raising the deployment target without a project requirement or user approval\n- Starting a new Objective-C integration when Swift is viable\n- Starting a new CocoaPods integration instead of Swift Package Manager\n- Accessing or customizing the backing `AVPlayer` instance instead of using Bitmovin Player APIs\n- Recreating the player during SwiftUI updates, fullscreen toggles, orientation changes, or routine source switches\n- Calling player APIs from an unsafe thread or actor context without SDK support\n- Treating every playback error as an SDK bug before checking logs and network requests\n- Ignoring license allowlist, bundle identifier, or entitlement mismatches across targets\n- Mixing SDK module versions or leaving a target without a required binary\u002Fmodule\n- Enabling `remoteControlConfig.isCastEnabled` without linking the Google Cast sender SDK to the app target\n- Adding a Google Cast SPM proxy as a nested repo\u002Fgitlink, which makes GitHub show `Subproject commit ...` instead of `Package.swift`\n- Forgetting `NSBonjourServices` and `NSLocalNetworkUsageDescription` for Cast discovery on iOS 14+\n- Defaulting to IMA when BAM \u002F Bitmovin advertising would satisfy the request\n- Adding `.ima` ad sources without linking the Google IMA SDK to the affected app target\n- Treating catalog ad markers or chapter metadata as actual VAST\u002FVMAP ad tags\n- Assuming iOS behavior automatically applies to tvOS or visionOS\n- Debugging HLS playback without validating the manifest when stream authoring looks suspicious\n- Hiding `mediastreamvalidator`, Charles, or log findings from the user because they are \"only warnings\"\n\n## Verification checklist\n\nAfter integration changes, verify each applicable item:\n\n1. **Build\u002Fcompile**\n   - The affected iOS, tvOS, and\u002For visionOS targets compile with the selected SDK modules.\n\n2. **Playback smoke test**\n   - A representative stream loads, starts, pauses, resumes, seeks when supported, and tears down cleanly.\n\n3. **Log health**\n   - Verbose\u002Fdebug logs are enabled during troubleshooting.\n   - No unexplained SDK errors, source-load failures, DRM\u002Fad failures, or lifecycle warnings remain.\n   - Advertising integrations log enough ad events or errors to prove the selected module is wired.\n   - Cast integrations log or visibly prove Cast manager initialization and device discovery when a receiver is available.\n\n4. **Network evidence**\n   - Manifest\u002Fsegment\u002Flicense\u002Fad requests look correct when inspected through available logs or Charles Proxy.\n\n5. **Stream validity**\n   - `mediastreamvalidator` has been run for suspicious HLS streams when available, and results are summarized to the user.\n\n6. **Lifecycle resilience**\n   - Background\u002Fforeground, navigation, fullscreen\u002FPiP\u002FAirPlay, and teardown paths do not recreate or leak players unexpectedly.\n\n7. **Platform UX**\n   - iOS touch controls, tvOS focus\u002Fremote behavior, and visionOS layout expectations are checked for the affected targets.\n\n## Response behavior while executing\n\n- Keep updates concise and evidence-led.\n- State which manifest-backed markdown document or SDK evidence was used when APIs are ambiguous.\n- When blocked, report the exact missing API, project wiring issue, log line, network result, or stream-validator finding.\n- Suggest the next smallest probe before proposing broad rewrites.\n","# Bitmovin Player Apple SDK Integration\n\n## API source of truth\n\nUse the versioned Bitmovin Player Apple SDK markdown manifest as the authoritative reference for API behavior, class names, method signatures, setup steps, availability, and feature integration:\n\n```text\nhttps:\u002F\u002Fcdn.bitmovin.com\u002Fplayer\u002Fios\u002F3\u002FBitmovinPlayer-markdown-manifest.json\n```\n\nVersion policy: if the app already uses a specific Bitmovin Player SDK version, prefer the matching versioned manifest when available. If the user has not specified a version or the matching manifest is unavailable, use the pinned manifest above until a newer source of truth is provided.\n\nLookup workflow:\n\n1. Fetch the manifest.\n2. Search `documents[]` by `title` or `identifier` for the API symbol\u002Farticle.\n3. Take the matching `identifier`, lowercase the full path, prefix it with `\u002Fdata`, and append `.md`.\n4. Fetch that markdown file from the same versioned CDN base.\n\nExample:\n\n```text\nidentifier: \u002Fdocumentation\u002FBitmovinPlayerCore\u002FPlayerEventsApi\nmarkdown:   https:\u002F\u002Fcdn.bitmovin.com\u002Fplayer\u002Fios\u002F3\u002Fdata\u002Fdocumentation\u002Fbitmovinplayercore\u002Fplayereventsapi.md\n```\n\nIf a markdown file is unavailable, fall back to the corresponding DocC JSON at the same `\u002Fdata\u002F...json` path and state that the answer is derived from DocC JSON rather than markdown.\n\nUse the samples repository as the implementation companion reference for wiring examples and end-to-end app setup:\n\n```text\nhttps:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fbitmovin-player-ios-samples\n```\n\nUse iOS\u002FtvOS\u002FvisionOS release notes to determine available versions, recent changes, migration notes, and the latest Bitmovin Player Apple SDK version:\n\n```text\nhttps:\u002F\u002Fdeveloper.bitmovin.com\u002Fplayback\u002Fdocs\u002Frelease-notes-ios.md\n```\n\nIf examples, local source, cached snippets, or prior assumptions conflict with the manifest-backed markdown docs, follow the markdown docs.\n\n## When to use this skill\n\nUse this skill when the task involves Bitmovin Player SDK integration or troubleshooting in an Apple-platform app:\n\n- Add or upgrade Bitmovin Player SDK in an iOS, tvOS, or visionOS app\n- Wire playback into SwiftUI, UIKit, or mixed Swift\u002FObjective-C code\n- Fix compile\u002Fruntime issues in player setup, source loading, events, ads, DRM, or analytics\n- Diagnose unexpected playback behavior, stalls, startup failures, stream issues, or network failures\n- Add or debug AirPlay, Google Cast \u002F Chromecast, Picture in Picture, fullscreen, background playback, or remote-control behavior\n- Adapt one playback pipeline across iPhone, iPad, Apple TV, and visionOS surfaces\n- Turn session-specific integration roadblocks into concise guidance that helps the agent reach a working integration faster\n\n## Supported deployment targets\n\nUnless the user or project explicitly requires a higher baseline, preserve these minimum deployment versions:\n\n- iOS 14+\n- tvOS 14+\n- visionOS 1+\n\n## Language policy\n\n- Prefer Swift for new integrations.\n- SwiftUI and UIKit are both valid depending on the host app.\n- Objective-C is supported for existing codebases and interoperability, but discourage starting a new Bitmovin Player integration in Objective-C unless the user explicitly asks or the project is already Objective-C-first.\n\n## Dependency installation\n\nUse Swift Package Manager for new integrations.\n\n### Swift Package Manager\n\nIn Xcode, add the Bitmovin Player SDK through Project > Package Dependencies with this package URL:\n\n```text\nhttps:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fplayer-ios.git\n```\n\nFor `Package.swift` based projects, add the package dependency and use the `BitmovinPlayer` product:\n\n```swift\ndependencies: [\n    .package(url: \"https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fplayer-ios.git\", exact: \"Version Number\")\n],\ntargets: [\n    .target(\n        name: \"\u003CNAME_OF_YOUR_PACKAGE>\",\n        dependencies: [\n            .product(name: \"BitmovinPlayer\", package: \"player-ios\")\n        ]\n    )\n]\n```\n\nReplace `Version Number` with the desired SDK version. If a user has not requested a specific version, verify the current recommended\u002Flatest version before choosing one.\n\nNote: command-line `swift build` is currently not supported for packages depending on `BitmovinPlayer`; open the package in Xcode instead.\n\n### CocoaPods\n\nCocoaPods is deprecated for this SDK and discouraged for new integrations. Only keep CocoaPods when maintaining an existing Podfile-based integration and the user explicitly wants to avoid migration. Prefer moving new work to Swift Package Manager.\n\n### Analytics \u002F Observability collector versioning\n\nThe analytics \u002F observability collector version can be updated independently from the player version. Do not force a Bitmovin Player SDK upgrade only to update the collector, as long as the selected collector satisfies the minimum version required by the installed player version.\n\n## Core principles\n\n1. **Verify APIs before editing**\n   - Resolve SDK-specific APIs through the API source-of-truth section before editing code.\n   - Before writing SDK-specific code, inspect the app's installed SDK version, dependency manager, generated symbols, local examples, and the matching manifest-backed markdown docs.\n   - When a requested feature is unfamiliar or the integration path is unclear, first search the matching API docs\u002Fmanifest for relevant symbols or articles. If docs are missing or ambiguous, inspect local generated headers\u002Fpackage symbols before writing code, and state the evidence used.\n   - Do not invent class names, initializers, config fields, or event names.\n\n2. **Use one stable playback pipeline**\n   - Keep player creation, source loading, event subscription, and teardown centralized.\n   - Vary platform UI around that core pipeline instead of forking playback logic per device class.\n\n3. **Respect Apple lifecycle boundaries**\n   - Keep player instances stable for a playback scope.\n   - Rebind views or controllers for UI changes instead of recreating the player during normal layout, navigation, fullscreen, or SwiftUI state changes.\n   - Destroy\u002Frelease player resources only when the playback context is finished.\n\n4. **Do not bypass the SDK**\n   - Never access or depend on the backing `AVPlayer` instance from the Bitmovin `Player` instance.\n   - Use documented Bitmovin Player APIs for playback state, control, events, source handling, and feature integration.\n   - If a use case appears to require direct `AVPlayer` access, treat that as a design roadblock and look for an SDK-supported API or escalate the missing capability to the user.\n\n5. **Start troubleshooting from evidence**\n   - Enable verbose\u002Fdebug logging before reproducing issues.\n   - Check SDK logs, app playback logs, event order, network requests, and stream validation output before changing code.\n   - Separate player integration bugs from stream authoring, license, DRM, network, and platform-lifecycle issues.\n\n6. **Derive roadblock-lifting guidance**\n   - When integration stalls, identify the exact roadblock: missing API knowledge, project wiring, platform entitlement, network visibility, stream validity, lifecycle timing, or test coverage.\n   - Convert verified findings into a compact task-local \"skill fragment\": what was checked, what was learned, the command or code path used, and what to avoid next.\n   - Do not persist new durable memory or skill files unless the user explicitly asks.\n\n## Integration workflow\n\n### 1) Gather project context first\n\nBefore code changes, inspect:\n\n- Target platforms: iOS, tvOS, visionOS, or shared Apple-platform module\n- Deployment targets, preserving iOS 14+, tvOS 14+, and visionOS 1+ unless the project already requires higher\n- UI stack: SwiftUI, UIKit, AppKit bridge, or mixed approach\n- Language boundary: Swift-first for new work; Objective-C only for existing Objective-C codebases or interoperability\n- Dependency manager: prefer Swift Package Manager; treat CocoaPods as legacy maintenance only\n- Installed Bitmovin Player SDK version and related modules\n- Analytics \u002F observability collector version, which can usually move independently as long as it satisfies the player's minimum collector requirement\n- Existing player wrapper\u002Fservice, DI boundary, event bus, analytics layer, or sample app pattern\n- License-key handling and bundle identifiers used by each target\n- Required features: VOD\u002Flive, DRM\u002FFairPlay, ads, analytics, subtitles, AirPlay, Google Cast, PiP, background playback, or platform-specific controls\n\n### 2) Validate dependency and target setup\n\n- Confirm each app target links the required Bitmovin SDK modules.\n- Keep SDK modules version-aligned.\n- Treat the analytics \u002F observability collector as independently updatable when it still satisfies the player SDK's minimum required collector version.\n- For new integrations, add `https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fplayer-ios.git` through Swift Package Manager and depend on the `BitmovinPlayer` product.\n- Avoid introducing CocoaPods; it is deprecated and should only remain for existing Podfile-based integrations when migration is out of scope.\n- Confirm deployment targets remain at or above the supported minimums: iOS 14, tvOS 14, visionOS 1.\n- Verify target platform support before wiring a feature into tvOS or visionOS.\n- Check app capabilities and entitlements when a feature depends on platform support.\n- Avoid broad project-file churn; update only the targets and package configuration required for the task.\n\n### 3) Build player construction deliberately\n\n- Create player\u002Fconfig objects in one clear owner, such as a playback service, view model, coordinator, or controller.\n- Keep license, playback, advertising, DRM, analytics, and style configuration explicit.\n- Avoid scattering one-off config mutation across SwiftUI views or UIKit lifecycle callbacks.\n- Treat config objects as construct-and-apply inputs unless current SDK docs explicitly recommend mutation.\n\n### 4) Attach UI without destabilizing playback\n\n- SwiftUI: use a stable representable\u002Fcoordinator boundary for SDK views or controllers.\n- UIKit: keep view-controller lifecycle, player ownership, and teardown explicit.\n- tvOS: preserve focus and remote-control behavior; do not assume touch-first controls map cleanly.\n- visionOS: confirm feature availability and layout expectations before reusing iOS-specific UI behavior.\n- Fullscreen\u002FPiP\u002FAirPlay should integrate with the SDK and platform lifecycle instead of causing player recreation.\n\n#### SwiftUI fullscreen gotcha\n\nThe SwiftUI `VideoPlayerView` wrapper is suitable for inline playback, but SDK fullscreen is not supported through that SwiftUI wrapper yet because it does not expose the underlying `PlayerView` \u002F `fullscreenHandler` surface.\n\nIf a SwiftUI app needs Bitmovin SDK fullscreen, wrap UIKit `PlayerView` in `UIViewRepresentable`, keep one stable `PlayerView` instance in the coordinator, set `playerView.fullscreenHandler`, and reparent that same view into a full-screen `UIViewController` on enter\u002Fexit. Do not create a second `Player` or replace the playback pipeline for fullscreen.\n\nFor fullscreen orientation behavior, check the app's supported interface orientations and make the presented fullscreen controller participate in rotation explicitly when needed.\n\n#### PiP and background playback recipe\n\nFor iOS Picture in Picture and background playback, wire all required layers together instead of setting only the SDK flag:\n\n- Enable background playback on the player config, for example `playerConfig.playbackConfig.isBackgroundPlaybackEnabled = true`.\n- Enable PiP on the player view config, for example `playerViewConfig.pictureInPictureConfig.isEnabled = true`.\n- If the product should enter PiP when backgrounded, verify and set the current SDK's auto-enter property, for example `playerViewConfig.pictureInPictureConfig.shouldEnterOnBackground = true`.\n- Configure `AVAudioSession` with a playback category such as `.playback` before playback starts.\n- Add `UIBackgroundModes` with `audio` to the app's Info.plist or target configuration.\n- After building, inspect the built app's Info.plist to confirm `UIBackgroundModes` is actually present as an array containing `audio`; generated Info.plist build settings may not preserve this correctly in every project setup.\n\n### 5) Add Google Cast deliberately\n\nGoogle Cast support is not just an SDK flag. Wire all three layers: Bitmovin Cast API, Google Cast sender SDK dependency, and iOS local-network discovery configuration.\n\nBefore editing:\n\n- Confirm the installed Bitmovin Player SDK version from `Package.resolved`, the project file, or the local package checkout.\n- Verify the current Cast API from manifest-backed docs or generated headers. For Bitmovin Player 3.113.x, the relevant surface is `BitmovinCastManager.initializeCasting(...)`, `PlayerConfig.remoteControlConfig.isCastEnabled`, and, for custom controls, `player.castVideo()` \u002F `player.castStop()`.\n- Check whether the project already has Google Cast linked through CocoaPods, a vendored framework, or a Swift package. Do not assume the `BitmovinPlayer` SPM product includes the Google Cast sender SDK.\n- If the user points to a local sample, compare that sample's package, Info.plist, app startup, and project-file wiring before inventing a new dependency shape.\n\nFor an SPM-based setup with the Google Cast binary SDK:\n\n1. Add a local Swift package such as `GoogleCastSPMProxy\u002FPackage.swift` or mirror the user's existing sample path. The package should expose a product named `GoogleCast` backed by a binary target named `GoogleCast`.\n2. Use Google's Cast SDK ZIP URL and checksum for the selected version. A previously validated local setup used `GoogleCastSDK-ios-4.8.4_dynamic.zip` with checksum `c9c3a794e8585198b59c6bb7da5418a3194ffa1ffa6f9a1cbdf4dc0ea26dc6cf`, but verify the current URL\u002Fchecksum before choosing a new version.\n3. Add the local package reference to the Xcode project and link the `GoogleCast` product into every app target that enables casting. Prefer Xcode\u002Fpackage tooling when available; if manually patching `project.pbxproj`, follow the file's existing `XCLocalSwiftPackageReference`, `XCSwiftPackageProductDependency`, `PBXBuildFile`, `packageReferences`, and `packageProductDependencies` patterns exactly.\n4. Ensure the proxy is a normal tracked folder, not a nested git repository or gitlink. If GitHub shows `Subproject commit ...`, run `git ls-files --stage \u003Cproxy-path> .gitmodules`; mode `160000` means SwiftPM files inside the folder will not be visible as normal repo files.\n\nTypical app-side wiring:\n\n```swift\nimport BitmovinPlayer\nimport SwiftUI\n\n@main\nstruct PlaybackApp: App {\n    init() {\n        BitmovinCastManager.initializeCasting()\n    }\n\n    var body: some Scene {\n        WindowGroup {\n            ContentView()\n        }\n    }\n}\n```\n\nEnable casting where the player config is created, keeping it near the existing playback configuration:\n\n```swift\nlet playerConfig = PlayerConfig()\nplayerConfig.key = licenseKey\nplayerConfig.remoteControlConfig.isCastEnabled = true\nlet player = PlayerFactory.createPlayer(playerConfig: playerConfig)\n```\n\nFor iOS 14+ local-network discovery, add Info.plist entries for Google Cast Bonjour services. For the default Bitmovin Cast receiver, include both the generic service and the receiver-specific service:\n\n```xml\n\u003Ckey>NSBonjourServices\u003C\u002Fkey>\n\u003Carray>\n    \u003Cstring>_googlecast._tcp\u003C\u002Fstring>\n    \u003Cstring>_FFE417E5._googlecast._tcp\u003C\u002Fstring>\n\u003C\u002Farray>\n\u003Ckey>NSLocalNetworkUsageDescription\u003C\u002Fkey>\n\u003Cstring>This app uses the local network to discover Google Cast devices.\u003C\u002Fstring>\n```\n\nIf the app uses a custom receiver app id, replace `FFE417E5` with that app id in the receiver-specific Bonjour entry after verifying the Bitmovin Cast initialization API for custom receivers.\n\nAfter wiring:\n\n- Run `plutil -lint` on edited plist files.\n- Build with Xcode\u002FXcode MCP so Swift package resolution, local binary artifacts, and target product linking are checked together.\n- Inspect `Package.resolved`: local packages may not add pins, so absence of a Google Cast pin is not automatically a failure.\n- Runtime validation still requires a discoverable Cast receiver on the same network; a successful build only proves the dependency and API wiring.\n\n### 6) Load sources explicitly\n\n- Use explicit source types when the stream format is known.\n- Keep source URL, stream type, DRM, ads, subtitle, thumbnail, and analytics metadata together.\n- For source switching, prefer a deliberate transition strategy instead of destroying\u002Frecreating the player for routine changes.\n- Add deterministic fallback only when the product needs multiple playback candidates.\n\n### 7) Add advertising deliberately\n\nThe Apple SDK supports two client-side advertising module paths:\n\n- BAM \u002F Bitmovin advertising: use Bitmovin ad sources, for example `AdSource(..., ofType: .bitmovin)`. Prefer this path unless the user explicitly asks for IMA or the existing app already uses IMA.\n- IMA advertising: use IMA ad sources, for example `AdSource(..., ofType: .ima)`. This requires the Google IMA SDK to be linked by the app target in addition to the Bitmovin Player SDK; do not assume the `BitmovinPlayer` SPM product alone provides Google IMA.\n\nBefore adding ads, identify the ad module, the ad tag type (VAST, VMAP, or another supported setup), and whether existing catalog metadata is placement-only. Fields such as `ad_markers_json` can describe where ads should appear, but they are not themselves VAST\u002FVMAP ad supply unless the referenced payload contains an actual ad tag\u002Fconfig.\n\nFor a BAM pre-roll, keep the setup close to player construction:\n\n```swift\nlet adSource = AdSource(tag: vastOrVmapTagUrl, ofType: .bitmovin)\nlet preRoll = AdItem(adSources: [adSource], atPosition: \"pre\")\nplayerConfig.advertisingConfig = AdvertisingConfig(schedule: [preRoll])\n```\n\nFor IMA, first verify the current Google IMA installation instructions and the exact package\u002Fproduct or framework used by the project. Then add the Google IMA dependency to the same app target that links `BitmovinPlayer`, use `.ima` ad sources, and build the target after package resolution. If the user did not ask for IMA, do not add Google IMA.\n\nTreat public sample VAST tags as placeholders only. Prefer product-owned ad tags\u002Fconfiguration when available, and make placeholder tags obvious in code or configuration.\n\n### 8) Listen to events with Combine\n\nPrefer the modern Combine-based event API for Swift integrations:\n\nSwift files that use `ObservableObject`, `@Published`, `AnyCancellable`, or `player.events.on(...)` publishers need `import Combine` explicitly. Do not assume `SwiftUI` or `Foundation` imports make those symbols available in every file.\n\n```swift\nimport Combine\n\nfinal class PlaybackObserver {\n    private var cancellables = Set\u003CAnyCancellable>()\n\n    func bind(to player: Player) {\n        player.events.on(ReadyEvent.self)\n            .sink { event in\n                \u002F\u002F Handle player readiness.\n            }\n            .store(in: &cancellables)\n\n        player.events.on(PlayerErrorEvent.self)\n            .sink { event in\n                \u002F\u002F Handle player errors.\n            }\n            .store(in: &cancellables)\n\n        player.events.on(SourceEvent.self)\n            .sink { event in\n                \u002F\u002F Handle events emitted by the current source through the player.\n            }\n            .store(in: &cancellables)\n    }\n}\n```\n\nSwiftUI can subscribe directly with `onReceive` when the subscription lifetime matches the view:\n\n```swift\nVideoPlayerView(player: player, playerViewConfig: playerViewConfig)\n    .onReceive(player.events.on(PlayerEvent.self)) { event in\n        \u002F\u002F Observe all player events.\n    }\n    .onReceive(player.events.on(SourceEvent.self)) { event in\n        \u002F\u002F Observe source events emitted through the player.\n    }\n```\n\nEvent API guidance:\n\n- Use `player.events.on(SomePlayerEvent.self)` for player events.\n- To receive all player-session events, subscribe to both `player.events.on(PlayerEvent.self)` and `player.events.on(SourceEvent.self)`.\n- Use `player.events.on(SourceEvent.self)` for events from the current source as seen through the player.\n- Use `source.events.on(SomeSourceEvent.self)` when observing a specific `Source` instance directly.\n- Use `playerView.events.on(SomePlayerViewEvent.self)` for Swift-only `PlayerView` UI events.\n- Store `AnyCancellable`s for as long as observation should remain active.\n- Keep event handling lightweight; dispatch heavier analysis or UI state updates through the app's normal model\u002Fcoordinator boundary.\n- Never use events as a reason to reach into the backing `AVPlayer`.\n\nLegacy and Objective-C guidance:\n\n- The Combine event APIs are Swift-only.\n- Existing Objective-C integrations and older Swift code can use listener protocols such as `PlayerListener`, `SourceListener`, and `UserInterfaceListener` through `add(listener:)` \u002F `remove(listener:)`.\n- For new Swift integrations, prefer Combine publishers instead of adding listener classes.\n- If listener protocols are used, remove listeners explicitly at the same lifecycle boundary where they were added.\n\n### 9) Instrument the session\n\nWhen diagnosing or integrating a non-trivial flow:\n\n- Enable SDK verbose\u002Fdebug logging before creating player\u002Fsource objects.\n- Add app-level logs around player creation, source loading, playback start, errors, retries, ad\u002FDRM setup, lifecycle transitions, and teardown.\n- Subscribe to the relevant Combine event publishers and log event order with timestamps.\n- Include source URL, source type, SDK version, target platform, and feature flags in bug reports when safe to share.\n\n### 10) Apply the roadblock loop\n\nIf the agent is stuck:\n\n1. Name the current blocker in one sentence.\n2. State what evidence exists and what is still missing.\n3. Choose the smallest next probe: search the matching API docs for unfamiliar features, inspect SDK symbols, check logs, reproduce with a known stream, inspect network, validate the HLS stream, or isolate lifecycle behavior.\n4. After the probe, write down the verified rule that would have avoided the block.\n5. Continue integration using that rule; do not expand scope unless the evidence requires it.\n\n## Troubleshooting runbook\n\n### Always check logs first\n\n- Ensure SDK verbose\u002Fdebug logging is enabled for the reproduction.\n- Collect Xcode console logs, device logs, and app playback logs.\n- Look first for license rejection, source-load failures, manifest parsing errors, DRM\u002FFairPlay failures, ad errors, network status codes, ATS issues, and lifecycle-related teardown\u002Freload ordering.\n- If logs are noisy, add a short app-specific playback\u002Fsession prefix and filter around the reproduction window.\n\n### Inspect network requests\n\n- Suggest setting up Charles Proxy to the user when network behavior is unclear.\n- Use it to inspect manifest, segment, subtitle, DRM certificate\u002Flicense, ad tag, analytics, and retry\u002Ffallback requests.\n- Confirm HTTP status codes, redirects, headers, CORS-like service behavior, TLS\u002FATS compatibility, and response timing.\n- Treat network traces as sensitive; do not ask the user to share tokens, license keys, or private URLs unless they can redact them.\n\n### Validate HLS streams with Apple tools\n\nFor unexpected HLS playback behavior, run `mediastreamvalidator` against the sample stream when it is installed:\n\n```shell\nmediastreamvalidator \"https:\u002F\u002Fexample.com\u002Fpath\u002Fto\u002Fmaster.m3u8\"\n```\n\n- If the tool is missing, tell the user and continue with available evidence.\n- Flag validator warnings\u002Ferrors to the user instead of silently treating them as SDK bugs.\n- Distinguish stream-authoring problems from player-integration problems before proposing code changes.\n\n### Reduce to a known-good comparison\n\n- Reproduce with a known-good Bitmovin or Apple sample stream when the user's stream may be invalid.\n- Reproduce the user's stream in a minimal sample when the app integration may be the issue.\n- Compare event order, network requests, and validator output between the known-good stream and the failing stream.\n\n## Common pitfalls to avoid\n\n- Guessing APIs without checking the manifest-backed markdown docs\n- Raising the deployment target without a project requirement or user approval\n- Starting a new Objective-C integration when Swift is viable\n- Starting a new CocoaPods integration instead of Swift Package Manager\n- Accessing or customizing the backing `AVPlayer` instance instead of using Bitmovin Player APIs\n- Recreating the player during SwiftUI updates, fullscreen toggles, orientation changes, or routine source switches\n- Calling player APIs from an unsafe thread or actor context without SDK support\n- Treating every playback error as an SDK bug before checking logs and network requests\n- Ignoring license allowlist, bundle identifier, or entitlement mismatches across targets\n- Mixing SDK module versions or leaving a target without a required binary\u002Fmodule\n- Enabling `remoteControlConfig.isCastEnabled` without linking the Google Cast sender SDK to the app target\n- Adding a Google Cast SPM proxy as a nested repo\u002Fgitlink, which makes GitHub show `Subproject commit ...` instead of `Package.swift`\n- Forgetting `NSBonjourServices` and `NSLocalNetworkUsageDescription` for Cast discovery on iOS 14+\n- Defaulting to IMA when BAM \u002F Bitmovin advertising would satisfy the request\n- Adding `.ima` ad sources without linking the Google IMA SDK to the affected app target\n- Treating catalog ad markers or chapter metadata as actual VAST\u002FVMAP ad tags\n- Assuming iOS behavior automatically applies to tvOS or visionOS\n- Debugging HLS playback without validating the manifest when stream authoring looks suspicious\n- Hiding `mediastreamvalidator`, Charles, or log findings from the user because they are \"only warnings\"\n\n## Verification checklist\n\nAfter integration changes, verify each applicable item:\n\n1. **Build\u002Fcompile**\n   - The affected iOS, tvOS, and\u002For visionOS targets compile with the selected SDK modules.\n\n2. **Playback smoke test**\n   - A representative stream loads, starts, pauses, resumes, seeks when supported, and tears down cleanly.\n\n3. **Log health**\n   - Verbose\u002Fdebug logs are enabled during troubleshooting.\n   - No unexplained SDK errors, source-load failures, DRM\u002Fad failures, or lifecycle warnings remain.\n   - Advertising integrations log enough ad events or errors to prove the selected module is wired.\n   - Cast integrations log or visibly prove Cast manager initialization and device discovery when a receiver is available.\n\n4. **Network evidence**\n   - Manifest\u002Fsegment\u002Flicense\u002Fad requests look correct when inspected through available logs or Charles Proxy.\n\n5. **Stream validity**\n   - `mediastreamvalidator` has been run for suspicious HLS streams when available, and results are summarized to the user.\n\n6. **Lifecycle resilience**\n   - Background\u002Fforeground, navigation, fullscreen\u002FPiP\u002FAirPlay, and teardown paths do not recreate or leak players unexpectedly.\n\n7. **Platform UX**\n   - iOS touch controls, tvOS focus\u002Fremote behavior, and visionOS layout expectations are checked for the affected targets.\n\n## Response behavior while executing\n\n- Keep updates concise and evidence-led.\n- State which manifest-backed markdown document or SDK evidence was used when APIs are ambiguous.\n- When blocked, report the exact missing API, project wiring issue, log line, network result, or stream-validator finding.\n- Suggest the next smallest probe before proposing broad rewrites.\n",{"name":5,"description":7},"main","14b211bb07d3e4057b85fb9140369fc73b1eb33b","skills\u002Fbitmovin-player-ios","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills","https:\u002F\u002Fgithub.com\u002Fbitmovin","https:\u002F\u002Fbitmovin.com","2026-09-11 10:36:33",{"source":8,"skillName":5,"commands":51,"prompt":61,"zipAvailable":62},[52,55,58],{"tool":53,"command":54},"npx","npx skills add bitmovin\u002Fskills --skill bitmovin-player-ios",{"tool":56,"command":57},"bunx","bunx skills add bitmovin\u002Fskills --skill bitmovin-player-ios",{"tool":59,"command":60},"pnpm","pnpm dlx skills add bitmovin\u002Fskills --skill bitmovin-player-ios","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-player-ios\u002FSKILL.md",true,[],[65,96,119,139,161,187],{"skillNo":66,"name":9,"nameZh":6,"description":67,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":68,"categories":69,"keywords":78,"publishedAt":38},"01M6XCZ365TGD9DCJG6Z45S0T4","Hub skill for Bitmovin video infrastructure. Use when the user mentions Bitmovin, asks about video playback, encoding, transcoding, streaming, player integration, video analytics, content delivery optimization, or video AI. Routes to the right Bitmovin product (Player, VOD\u002FLive Encoding, Observability, AI Scene Analysis, Streams, Stream Lab) and walks through MCP\u002FCLI\u002FSDK setup.","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin\u002FSKILL.md",[70,75],{"categoryNo":71,"categoryName":72,"taxonomyType":73,"taxonomyTypeDesc":74},"01JBSEEDCAT000000000000016","图像与多媒体","SCENARIO","使用场景",{"categoryNo":76,"categoryName":77,"taxonomyType":73,"taxonomyTypeDesc":74},"01JBSEEDCAT000000000000023","Skill与智能体开发",[9,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,21,26,95],"MCP","cli","analytics","infrastructure","product","observability","streaming","Optimization","analysis","Video","use","sdk setup","delivery","live","streams","lab","when",{"skillNo":97,"name":98,"nameZh":6,"description":99,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":100,"categories":101,"keywords":105,"publishedAt":38},"01M6XCYXP57QRYXCW7QN1HXNVC","bitmovin-player-web","Integrate the Bitmovin Web Player SDK into a web app. Use when the user asks to add video playback, embed a player, play HLS\u002FDASH\u002FMP4\u002FWHEP\u002FMOQ, set up DRM (Widevine\u002FPlayReady\u002FFairPlay), integrate ads, customize the Player UI, or work with the Bitmovin Player in any way. Covers both Player v8 (stable) and Player Web X \u002F PWX (next-gen).","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-player-web\u002FSKILL.md",[102],{"categoryNo":103,"categoryName":104,"taxonomyType":73,"taxonomyTypeDesc":74},"01JBSEEDCAT000000000000009","前端开发",[106,21,107,9,108,109,110,88,33,111,112,89,30,113,114,115,116,117,118,95],"web","bitmovin player web","play","ui","SET","mp4","work","up","next","app","add","dash","hls",{"skillNo":120,"name":121,"nameZh":6,"description":122,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":123,"categories":124,"keywords":125,"publishedAt":38},"01M6XCYM297KGT0QKD7J9DNC4G","bitmovin-player-android","Integrate and troubleshoot the Bitmovin Android Player SDK in Kotlin\u002FCompose Android apps, including playback setup, Web UI, PiP\u002Ffullscreen handlers, media session, cast, TV variants, and source-loading robustness.","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-player-android\u002FSKILL.md",[],[126,21,127,9,128,106,109,129,130,131,30,132,133,28,134,135,27,136,137,138],"android","bitmovin player android","troubleshoot","Media","kotlin","session","compose","cast","web ui","loading","source","robustness","variants",{"skillNo":140,"name":141,"nameZh":6,"description":142,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":143,"categories":144,"keywords":146,"publishedAt":38},"01M6XCYCHP945BKSJ37BMDR0KR","bitmovin-observability-root-cause-analysis","Root-cause analysis for a video QoE metric that moved — startup time, rebuffer ratio, error rate, bitrate, video-start-failures — using the Bitmovin Observability MCP. Use whenever someone asks WHY a QoE metric changed, spiked, dropped, regressed, or \"looks off\", whether a release or CDN change hurt quality, or what drives a delta between two windows or cohorts. Trigger even without \"RCA\" — \"rebuffering is up in APAC\", \"did v11 regress startup?\", \"what happened to error rate Tuesday\" all belong here. Drives a disciplined Define→Gather→Reflect→Conclude investigation with mix-vs-rate (Simpson's paradox) protection and a falsification step, producing a structured findings object.","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-observability-root-cause-analysis\u002FSKILL.md",[145],{"categoryNo":76,"categoryName":77,"taxonomyType":73,"taxonomyTypeDesc":74},[147,84,87,148,149,9,150,79,151,152,153,154,88,155,113,156,157,158,159,160],"root cause analysis","root","root cause","bitmovin observability root cause analysis","change","reflect","release","start","why","time","error","mix","metric","rate",{"skillNo":162,"name":163,"nameZh":6,"description":164,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":165,"categories":166,"keywords":175,"publishedAt":38},"01M6XCY63KD9ZQ5S5P4KSV881A","bitmovin-observability-collector-integration","Integrate the Bitmovin Observability (Analytics) collector into any video player on any platform. Use WHENEVER a developer wants to add Bitmovin Analytics \u002F Observability tracking, \"set up the collector\", \"enable analytics\", \"track playback \u002F QoE \u002F impressions\", wire up a player to the Bitmovin dashboard, or asks why their analytics data is missing, empty, or wrong after integrating. Covers Web, Android\u002FFire TV, iOS\u002FtvOS\u002FvisionOS, and Roku, for both the Bitmovin Player and third-party engines (HLS.js, Shaka, Video.js, HTML5, ExoPlayer, Media3, AVPlayer, THEOplayer, CAFv3). Use it even when the user only names their player (\"add analytics to my Shaka player\") without saying \"Bitmovin collector\". Knows that the Bitmovin Player has analytics pre-integrated while third-party players need a separate collector with a managed attach\u002Fdetach lifecycle.","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-observability-collector-integration\u002FSKILL.md",[167,170],{"categoryNo":168,"categoryName":169,"taxonomyType":73,"taxonomyTypeDesc":74},"01JBSEEDCAT000000000000013","分析与可视化",{"categoryNo":171,"categoryName":172,"taxonomyType":173,"taxonomyTypeDesc":174},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[84,176,177,9,178,179,180,110,88,155,181,30,182,113,183,21,184,185,186,95],"integration","collector","bitmovin observability collector integration","dashboard","data","video player","only","attach","my","track","managed",{"skillNo":188,"name":189,"nameZh":6,"description":190,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":191,"categories":192,"keywords":198,"publishedAt":38},"01M6XCXZGGXA2YFFEVAC4B7PD3","bitmovin-observability-ad-report","Snapshot-style ad operations report for a Bitmovin Analytics license. Use whenever someone asks for an \"ad ops report\", \"ad health check\", \"ad funnel\", \"weekly ad review\", \"VAST error breakdown\", \"advertiser performance\", \"ad system comparison\", \"ad startup-time breakdown\", or \"how are our ads doing\" — even without the word \"report\". Builds the impression→start→quartile→ completion funnel, ranks advertisers and ad systems, flags VAST\u002Fad-server errors, evaluates pod-position cohorts, and summarizes ad startup latency as structured anomaly flags (AdOpsReport object). Does NOT explain why a metric moved between two windows — use the QoE RCA skill for that.","https:\u002F\u002Fgithub.com\u002Fbitmovin\u002Fskills\u002Fblob\u002Fmain\u002Fskills\u002Fbitmovin-observability-ad-report\u002FSKILL.md",[193,194,195],{"categoryNo":168,"categoryName":169,"taxonomyType":73,"taxonomyTypeDesc":74},{"categoryNo":171,"categoryName":172,"taxonomyType":173,"taxonomyTypeDesc":174},{"categoryNo":196,"categoryName":197,"taxonomyType":173,"taxonomyTypeDesc":174},"01JBSEEDCAT000000000000034","行政与办公人员",[199,84,200,9,201,202,203,81,204,154,205,155,33,206,207,156,157,208,209,159],"report","ad","bitmovin observability ad report","review","performance","server","how","snapshot","flags","errors","weekly"]