From dc1fe8a1119cd6e8c6b7aa0e79557320de847f65 Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Tue, 25 Aug 2026 18:29:28 +0530 Subject: [PATCH] Add UIKit and SwiftUI AVPlayer examples Rename example/ to Examples/UIKit, point it at the local package, and add a SwiftUI example. --- .gitignore | 8 + Examples/README.md | 12 + .../project.pbxproj | 243 ++++++++++++++++++ .../contents.xcworkspacedata | 0 .../SwiftUI/AVPlayerSwiftUIExample/App.swift | 12 + .../AVPlayerSwiftUIExample/HomeView.swift | 123 +++++++++ .../AVPlayerSwiftUIExample/PlayerView.swift | 127 +++++++++ Examples/SwiftUI/README.md | 40 +++ .../AVPlayerExample.xcodeproj/project.pbxproj | 22 +- .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 0 .../UIKit}/AVPlayerExample/AppDelegate.swift | 0 .../AccentColor.colorset/Contents.json | 0 .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 .../AVPlayerExample/HomeViewController.swift | 0 .../UIKit}/AVPlayerExample/Info.plist | 0 .../PlayerViewController.swift | 0 .../AVPlayerExample/SceneDelegate.swift | 0 Examples/UIKit/README.md | 36 +++ .../xcshareddata/swiftpm/Package.resolved | 24 -- .../UserInterfaceState.xcuserstate | Bin 24428 -> 0 bytes .../xcschemes/xcschememanagement.plist | 14 - example/README.md | 129 ---------- 24 files changed, 617 insertions(+), 180 deletions(-) create mode 100644 .gitignore create mode 100644 Examples/README.md create mode 100644 Examples/SwiftUI/AVPlayerSwiftUIExample.xcodeproj/project.pbxproj rename {example/AVPlayerExample.xcodeproj => Examples/SwiftUI/AVPlayerSwiftUIExample.xcodeproj}/project.xcworkspace/contents.xcworkspacedata (100%) create mode 100644 Examples/SwiftUI/AVPlayerSwiftUIExample/App.swift create mode 100644 Examples/SwiftUI/AVPlayerSwiftUIExample/HomeView.swift create mode 100644 Examples/SwiftUI/AVPlayerSwiftUIExample/PlayerView.swift create mode 100644 Examples/SwiftUI/README.md rename {example => Examples/UIKit}/AVPlayerExample.xcodeproj/project.pbxproj (94%) create mode 100644 Examples/UIKit/AVPlayerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename {example => Examples/UIKit}/AVPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist (100%) rename {example => Examples/UIKit}/AVPlayerExample/AppDelegate.swift (100%) rename {example => Examples/UIKit}/AVPlayerExample/Assets.xcassets/AccentColor.colorset/Contents.json (100%) rename {example => Examples/UIKit}/AVPlayerExample/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename {example => Examples/UIKit}/AVPlayerExample/Assets.xcassets/Contents.json (100%) rename {example => Examples/UIKit}/AVPlayerExample/HomeViewController.swift (100%) rename {example => Examples/UIKit}/AVPlayerExample/Info.plist (100%) rename {example => Examples/UIKit}/AVPlayerExample/PlayerViewController.swift (100%) rename {example => Examples/UIKit}/AVPlayerExample/SceneDelegate.swift (100%) create mode 100644 Examples/UIKit/README.md delete mode 100644 example/AVPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 example/AVPlayerExample.xcodeproj/project.xcworkspace/xcuserdata/d.nehareddy.xcuserdatad/UserInterfaceState.xcuserstate delete mode 100644 example/AVPlayerExample.xcodeproj/xcuserdata/d.nehareddy.xcuserdatad/xcschemes/xcschememanagement.plist delete mode 100644 example/README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6fb2a4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# Xcode +xcuserdata/ +*.xcuserstate +.build/ +.DS_Store + +# SwiftPM resolved (regenerated per checkout for local-package examples) +**/xcshareddata/swiftpm/Package.resolved diff --git a/Examples/README.md b/Examples/README.md new file mode 100644 index 0000000..9b65919 --- /dev/null +++ b/Examples/README.md @@ -0,0 +1,12 @@ +# FastPix Video Data AVPlayer — Examples + +Two standalone apps showing the **FastPix Video Data AVPlayer SDK** attached to +`AVPlayer`, one per UI paradigm: + +- [`UIKit/`](UIKit) — `AVPlayerViewController` (iOS 14+) +- [`SwiftUI/`](SwiftUI) — `VideoPlayer` (iOS 16+) + +Each folder is self-contained — open its `.xcodeproj`, press **▶ Run**, then +watch views on [dashboard.fastpix.com](https://dashboard.fastpix.com). Both +reference the SDK as a local Swift package (`../..`), so they build against this +checkout. See each project's README for details. diff --git a/Examples/SwiftUI/AVPlayerSwiftUIExample.xcodeproj/project.pbxproj b/Examples/SwiftUI/AVPlayerSwiftUIExample.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e42500d --- /dev/null +++ b/Examples/SwiftUI/AVPlayerSwiftUIExample.xcodeproj/project.pbxproj @@ -0,0 +1,243 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + C0000000000000000000B001 /* FastpixVideoDataAVPlayer in Frameworks */ = {isa = PBXBuildFile; productRef = C0000000000000000000A001 /* FastpixVideoDataAVPlayer */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + C0000000000000000000C001 /* AVPlayerSwiftUIExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AVPlayerSwiftUIExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + C0000000000000000000D001 /* AVPlayerSwiftUIExample */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = AVPlayerSwiftUIExample; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + C0000000000000000000E001 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C0000000000000000000B001 /* FastpixVideoDataAVPlayer in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C0000000000000000000F001 = { + isa = PBXGroup; + children = ( + C0000000000000000000D001 /* AVPlayerSwiftUIExample */, + C00000000000000000010001 /* Products */, + ); + sourceTree = ""; + }; + C00000000000000000010001 /* Products */ = { + isa = PBXGroup; + children = ( + C0000000000000000000C001 /* AVPlayerSwiftUIExample.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C00000000000000000011001 /* AVPlayerSwiftUIExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = C00000000000000000012001 /* Build configuration list for PBXNativeTarget "AVPlayerSwiftUIExample" */; + buildPhases = ( + C00000000000000000013001 /* Sources */, + C0000000000000000000E001 /* Frameworks */, + C00000000000000000014001 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + C0000000000000000000D001 /* AVPlayerSwiftUIExample */, + ); + name = AVPlayerSwiftUIExample; + packageProductDependencies = ( + C0000000000000000000A001 /* FastpixVideoDataAVPlayer */, + ); + productName = AVPlayerSwiftUIExample; + productReference = C0000000000000000000C001 /* AVPlayerSwiftUIExample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C00000000000000000015001 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 2660; + LastUpgradeCheck = 2660; + TargetAttributes = { + C00000000000000000011001 = { + CreatedOnToolsVersion = 26.6; + }; + }; + }; + buildConfigurationList = C00000000000000000016001 /* Build configuration list for PBXProject "AVPlayerSwiftUIExample" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = C0000000000000000000F001; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + C00000000000000000017001 /* XCLocalSwiftPackageReference "../.." */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = C00000000000000000010001 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C00000000000000000011001 /* AVPlayerSwiftUIExample */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + C00000000000000000014001 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C00000000000000000013001 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + C00000000000000000018001 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + C00000000000000000019001 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + C0000000000000000001A001 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOLS = NO; + CODE_SIGNING_ALLOWED = NO; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.fastpix.datasdk.avplayer.swiftui; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + C0000000000000000001B001 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOLS = NO; + CODE_SIGNING_ALLOWED = NO; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.fastpix.datasdk.avplayer.swiftui; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C00000000000000000016001 /* Build configuration list for PBXProject "AVPlayerSwiftUIExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C00000000000000000018001 /* Debug */, + C00000000000000000019001 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C00000000000000000012001 /* Build configuration list for PBXNativeTarget "AVPlayerSwiftUIExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C0000000000000000001A001 /* Debug */, + C0000000000000000001B001 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + C00000000000000000017001 /* XCLocalSwiftPackageReference "../.." */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "../.."; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + C0000000000000000000A001 /* FastpixVideoDataAVPlayer */ = { + isa = XCSwiftPackageProductDependency; + productName = FastpixVideoDataAVPlayer; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = C00000000000000000015001 /* Project object */; +} diff --git a/example/AVPlayerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/SwiftUI/AVPlayerSwiftUIExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/AVPlayerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Examples/SwiftUI/AVPlayerSwiftUIExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/Examples/SwiftUI/AVPlayerSwiftUIExample/App.swift b/Examples/SwiftUI/AVPlayerSwiftUIExample/App.swift new file mode 100644 index 0000000..f92fc15 --- /dev/null +++ b/Examples/SwiftUI/AVPlayerSwiftUIExample/App.swift @@ -0,0 +1,12 @@ +import SwiftUI + +@main +struct AVPlayerSwiftUIExampleApp: App { + var body: some Scene { + WindowGroup { + NavigationStack { + HomeView() + } + } + } +} diff --git a/Examples/SwiftUI/AVPlayerSwiftUIExample/HomeView.swift b/Examples/SwiftUI/AVPlayerSwiftUIExample/HomeView.swift new file mode 100644 index 0000000..aae08da --- /dev/null +++ b/Examples/SwiftUI/AVPlayerSwiftUIExample/HomeView.swift @@ -0,0 +1,123 @@ +import SwiftUI + +/// A sample HLS stream shown on the home screen. +struct SampleVideo: Identifiable, Hashable { + let id: String + let title: String + let subtitle: String + let duration: String + let url: URL +} + +enum Theme { + static let accent = Color(red: 0.235, green: 0.353, blue: 0.937) + static let accentDark = Color(red: 0.145, green: 0.204, blue: 0.639) +} + +/// Replace with the Workspace Key from your FastPix dashboard (Workspaces). +let workspaceKey = "1029207880411545601" +var isTracking: Bool { workspaceKey != "YOUR_WORKSPACE_KEY" && !workspaceKey.isEmpty } + +private let samples: [SampleVideo] = [ + SampleVideo( + id: "sample-video-001", + title: "Big Buck Bunny", + subtitle: "Blender Foundation · HLS", + duration: "9:56", + url: URL(string: "https://stream.fastpix.io/16ac212a-0f4f-49c5-9fd7-a42d9ff61541.m3u8")! + ), + SampleVideo( + id: "sample-video-002", + title: "Apple Basic Stream", + subtitle: "Multi-variant · HLS", + duration: "10:34", + url: URL(string: "https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8")! + ) +] + +/// Landing screen listing sample streams. Selecting one opens `PlayerView`, +/// where the FastPix Video Data AVPlayer SDK is attached. +struct HomeView: View { + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 16) { + headerCard + Text("SAMPLE STREAMS") + .font(.system(size: 13, weight: .semibold)) + .foregroundStyle(.secondary) + .padding(.horizontal, 4) + ForEach(samples) { video in + NavigationLink(value: video) { + VideoCard(video: video) + } + .buttonStyle(.plain) + } + } + .padding(16) + } + .background(Color(.systemGroupedBackground)) + .navigationTitle("FastPix Demo") + .navigationDestination(for: SampleVideo.self) { PlayerView(video: $0) } + } + + private var headerCard: some View { + VStack(alignment: .leading, spacing: 6) { + Image(systemName: "waveform.badge.magnifyingglass") + .font(.system(size: 24, weight: .semibold)) + .foregroundStyle(.white) + .frame(width: 44, height: 44) + .background(.white.opacity(0.18), in: RoundedRectangle(cornerRadius: 12)) + .padding(.bottom, 10) + Text("Video Data · AVPlayer") + .font(.system(size: 22, weight: .bold)) + .foregroundStyle(.white) + Text("Pick a stream to play. The FastPix SDK is attached to AVPlayer and streams real-time playback analytics to your dashboard.") + .font(.subheadline) + .foregroundStyle(.white.opacity(0.9)) + } + .padding(20) + .frame(maxWidth: .infinity, alignment: .leading) + .background( + LinearGradient(colors: [Theme.accent, Theme.accentDark], + startPoint: .topLeading, endPoint: .bottomTrailing), + in: RoundedRectangle(cornerRadius: 20, style: .continuous) + ) + } +} + +private struct VideoCard: View { + let video: SampleVideo + + var body: some View { + HStack(spacing: 14) { + ZStack { + LinearGradient(colors: [Theme.accent, Theme.accentDark], + startPoint: .topLeading, endPoint: .bottomTrailing) + Image(systemName: "play.fill") + .font(.system(size: 22)) + .foregroundStyle(.white) + } + .frame(width: 108, height: 68) + .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous)) + + VStack(alignment: .leading, spacing: 4) { + Text(video.title) + .font(.system(size: 17, weight: .semibold)) + .foregroundStyle(.primary) + Text(video.subtitle) + .font(.footnote) + .foregroundStyle(.secondary) + Text(video.duration) + .font(.system(size: 12, weight: .semibold)) + .foregroundStyle(Theme.accent) + .padding(.horizontal, 8).padding(.vertical, 2) + .background(Theme.accent.opacity(0.12), in: Capsule()) + .padding(.top, 2) + } + Spacer(minLength: 0) + } + .padding(12) + .background(Color(.secondarySystemGroupedBackground), + in: RoundedRectangle(cornerRadius: 16, style: .continuous)) + } +} diff --git a/Examples/SwiftUI/AVPlayerSwiftUIExample/PlayerView.swift b/Examples/SwiftUI/AVPlayerSwiftUIExample/PlayerView.swift new file mode 100644 index 0000000..b7da96c --- /dev/null +++ b/Examples/SwiftUI/AVPlayerSwiftUIExample/PlayerView.swift @@ -0,0 +1,127 @@ +import SwiftUI +import AVKit +import FastpixVideoDataAVPlayer + +/// Owns the `AVPlayer` and the FastPix tracking object for one playback session. +/// +/// The player and the SDK must live here — in an `ObservableObject` held by +/// `@StateObject` — not in the `View` struct, which SwiftUI recreates on every +/// render. `attach` wires the SDK to the same `AVPlayer` the `VideoPlayer` shows; +/// `teardown` stops tracking when the screen goes away. +@MainActor +final class PlayerModel: ObservableObject { + let player: AVPlayer + private let fpDataSDK = initAvPlayerTracking() + private var started = false + + init(url: URL) { + player = AVPlayer(url: url) + } + + func attach(video: SampleVideo) { + guard !started, isTracking else { player.play(); return } + started = true + + // All metadata fields must live under the "data" key. + let customMetadata: [String: Any] = ["data": [ + "workspace_id": workspaceKey, + "video_title": video.title, + "video_id": video.id, + "viewer_id": "user-12345", + "video_content_type": "movie", + "video_stream_type": "on-demand", + "player_name": "AVPlayerSwiftUIExample" + ]] + + // SwiftUI's VideoPlayer has no AVPlayerLayer, so pass the player directly. + fpDataSDK.trackAvPlayer(player: player, playerLayer: nil, customMetadata: customMetadata) + player.play() + } + + func teardown() { + player.pause() + // Stops the SDK's periodic time observer and internal timer. + fpDataSDK.resetInitialization() + } +} + +/// Plays a stream in a SwiftUI `VideoPlayer` with the FastPix SDK attached. +struct PlayerView: View { + let video: SampleVideo + @StateObject private var model: PlayerModel + + init(video: SampleVideo) { + self.video = video + _model = StateObject(wrappedValue: PlayerModel(url: video.url)) + } + + var body: some View { + ScrollView { + VStack(spacing: 16) { + VideoPlayer(player: model.player) + .aspectRatio(16.0 / 9.0, contentMode: .fit) + .clipShape(RoundedRectangle(cornerRadius: 16, style: .continuous)) + .shadow(color: .black.opacity(0.18), radius: 12, y: 6) + + statusPill + metadataCard + } + .padding(16) + } + .background(Color(.systemGroupedBackground)) + .navigationTitle(video.title) + .navigationBarTitleDisplayMode(.inline) + .onAppear { model.attach(video: video) } + .onDisappear { model.teardown() } + } + + private var statusPill: some View { + let color: Color = isTracking ? .green : .orange + return HStack(spacing: 10) { + Circle().fill(color).frame(width: 8, height: 8) + Text(isTracking ? "FastPix tracking active" : "Add your Workspace Key to start tracking") + .font(.system(size: 14, weight: .semibold)) + .foregroundStyle(color) + Spacer(minLength: 0) + } + .padding(.horizontal, 16).frame(height: 40) + .background(color.opacity(0.12), in: RoundedRectangle(cornerRadius: 14, style: .continuous)) + } + + private var metadataCard: some View { + let rows: [(String, String)] = [ + ("workspace_id", workspaceKey), + ("video_id", video.id), + ("video_title", video.title), + ("video_content_type", "movie"), + ("video_stream_type", "on-demand"), + ("viewer_id", "user-12345"), + ("player_name", "AVPlayerSwiftUIExample") + ] + return VStack(alignment: .leading, spacing: 12) { + Text("TRACKED METADATA") + .font(.system(size: 13, weight: .semibold)) + .foregroundStyle(.secondary) + VStack(spacing: 0) { + ForEach(Array(rows.enumerated()), id: \.offset) { index, row in + HStack { + Text(row.0) + .font(.system(size: 13, design: .monospaced)) + .foregroundStyle(.secondary) + Spacer(minLength: 12) + Text(row.1) + .font(.system(size: 14, weight: .medium)) + .foregroundStyle(.primary) + .multilineTextAlignment(.trailing) + } + .padding(.vertical, 11) + if index < rows.count - 1 { Divider() } + } + } + } + .padding(16) + .frame(maxWidth: .infinity, alignment: .leading) + .background(Color(.secondarySystemGroupedBackground), + in: RoundedRectangle(cornerRadius: 16, style: .continuous)) + } +} diff --git a/Examples/SwiftUI/README.md b/Examples/SwiftUI/README.md new file mode 100644 index 0000000..bddfb20 --- /dev/null +++ b/Examples/SwiftUI/README.md @@ -0,0 +1,40 @@ +# FastPix Video Data AVPlayer — SwiftUI Example + +A SwiftUI `VideoPlayer` with the FastPix Video Data AVPlayer SDK attached. + +## Run + +```bash +open AVPlayerSwiftUIExample.xcodeproj +``` + +Pick a Simulator (or your device), press **▶ Run**, tap a sample stream, then +open [dashboard.fastpix.com](https://dashboard.fastpix.com) → your workspace to +watch views appear. The project references the SDK as a local Swift package at +`../..`, so it builds against this checkout — no network fetch. + +## Files + +| File | Role | +|------|------| +| `PlayerView.swift` | Owns the `AVPlayer` + tracking object in an `ObservableObject` (`@StateObject`); attaches with `trackAvPlayer(player:playerLayer:nil)` in `.onAppear` and tears down in `.onDisappear`. | +| `HomeView.swift` | Landing list of sample streams; also holds `workspaceKey`. | +| `App.swift` | App entry point. | + +> The player and tracking object live in an `ObservableObject`, **not** the View +> struct — SwiftUI recreates View structs on every render, which would churn the +> player. `VideoPlayer` has no `AVPlayerLayer`, so the SDK gets the `AVPlayer` +> directly (`playerLayer: nil`). + +## Set your own workspace + stream + +Edit `HomeView.swift`: + +```swift +/// Replace with the Workspace Key from your FastPix dashboard (Workspaces). +let workspaceKey = "1029207880411545601" +``` + +Replace `workspaceKey` (dashboard.fastpix.com → **Workspaces**), and edit the +`samples` list to play your own HLS URLs. Without a valid key the app still plays +video but shows an orange "Add your Workspace Key" badge and sends no analytics. diff --git a/example/AVPlayerExample.xcodeproj/project.pbxproj b/Examples/UIKit/AVPlayerExample.xcodeproj/project.pbxproj similarity index 94% rename from example/AVPlayerExample.xcodeproj/project.pbxproj rename to Examples/UIKit/AVPlayerExample.xcodeproj/project.pbxproj index 6c72529..fbde73f 100644 --- a/example/AVPlayerExample.xcodeproj/project.pbxproj +++ b/Examples/UIKit/AVPlayerExample.xcodeproj/project.pbxproj @@ -122,7 +122,7 @@ ); mainGroup = 2A0000000000000000000001; packageReferences = ( - 7A0000000000000000000001 /* XCRemoteSwiftPackageReference "iOS-data-avplayer-sdk" */, + 7A0000000000000000000001 /* XCLocalSwiftPackageReference "../.." */, ); productRefGroup = 2A0000000000000000000003 /* Products */; projectDirPath = ""; @@ -213,7 +213,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = TRG6Q7877U; + CODE_SIGNING_ALLOWED = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AVPlayerExample/Info.plist; INFOPLIST_KEY_UILaunchScreen_Generation = NO; @@ -236,7 +236,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = TRG6Q7877U; + CODE_SIGNING_ALLOWED = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = AVPlayerExample/Info.plist; INFOPLIST_KEY_UILaunchScreen_Generation = NO; @@ -276,21 +276,17 @@ }; /* End XCConfigurationList section */ -/* Begin XCRemoteSwiftPackageReference section */ - 7A0000000000000000000001 /* XCRemoteSwiftPackageReference "iOS-data-avplayer-sdk" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/FastPix/iOS-data-avplayer-sdk.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.0.0; - }; +/* Begin XCLocalSwiftPackageReference section */ + 7A0000000000000000000001 /* XCLocalSwiftPackageReference "../.." */ = { + isa = XCLocalSwiftPackageReference; + relativePath = "../.."; }; -/* End XCRemoteSwiftPackageReference section */ +/* End XCLocalSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ 7A0000000000000000000002 /* FastpixVideoDataAVPlayer */ = { isa = XCSwiftPackageProductDependency; - package = 7A0000000000000000000001 /* XCRemoteSwiftPackageReference "iOS-data-avplayer-sdk" */; + package = 7A0000000000000000000001 /* XCLocalSwiftPackageReference "../.." */; productName = FastpixVideoDataAVPlayer; }; /* End XCSwiftPackageProductDependency section */ diff --git a/Examples/UIKit/AVPlayerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Examples/UIKit/AVPlayerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Examples/UIKit/AVPlayerExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example/AVPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/UIKit/AVPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/AVPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Examples/UIKit/AVPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/AVPlayerExample/AppDelegate.swift b/Examples/UIKit/AVPlayerExample/AppDelegate.swift similarity index 100% rename from example/AVPlayerExample/AppDelegate.swift rename to Examples/UIKit/AVPlayerExample/AppDelegate.swift diff --git a/example/AVPlayerExample/Assets.xcassets/AccentColor.colorset/Contents.json b/Examples/UIKit/AVPlayerExample/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from example/AVPlayerExample/Assets.xcassets/AccentColor.colorset/Contents.json rename to Examples/UIKit/AVPlayerExample/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/example/AVPlayerExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/UIKit/AVPlayerExample/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/AVPlayerExample/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Examples/UIKit/AVPlayerExample/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/AVPlayerExample/Assets.xcassets/Contents.json b/Examples/UIKit/AVPlayerExample/Assets.xcassets/Contents.json similarity index 100% rename from example/AVPlayerExample/Assets.xcassets/Contents.json rename to Examples/UIKit/AVPlayerExample/Assets.xcassets/Contents.json diff --git a/example/AVPlayerExample/HomeViewController.swift b/Examples/UIKit/AVPlayerExample/HomeViewController.swift similarity index 100% rename from example/AVPlayerExample/HomeViewController.swift rename to Examples/UIKit/AVPlayerExample/HomeViewController.swift diff --git a/example/AVPlayerExample/Info.plist b/Examples/UIKit/AVPlayerExample/Info.plist similarity index 100% rename from example/AVPlayerExample/Info.plist rename to Examples/UIKit/AVPlayerExample/Info.plist diff --git a/example/AVPlayerExample/PlayerViewController.swift b/Examples/UIKit/AVPlayerExample/PlayerViewController.swift similarity index 100% rename from example/AVPlayerExample/PlayerViewController.swift rename to Examples/UIKit/AVPlayerExample/PlayerViewController.swift diff --git a/example/AVPlayerExample/SceneDelegate.swift b/Examples/UIKit/AVPlayerExample/SceneDelegate.swift similarity index 100% rename from example/AVPlayerExample/SceneDelegate.swift rename to Examples/UIKit/AVPlayerExample/SceneDelegate.swift diff --git a/Examples/UIKit/README.md b/Examples/UIKit/README.md new file mode 100644 index 0000000..ae9f746 --- /dev/null +++ b/Examples/UIKit/README.md @@ -0,0 +1,36 @@ +# FastPix Video Data AVPlayer — UIKit Example + +An `AVPlayerViewController` with the FastPix Video Data AVPlayer SDK attached. + +## Run + +```bash +open AVPlayerExample.xcodeproj +``` + +Pick a Simulator (or your device), press **▶ Run**, tap a sample stream, then +open [dashboard.fastpix.com](https://dashboard.fastpix.com) → your workspace to +watch views appear. The project references the SDK as a local Swift package at +`../..`, so it builds against this checkout — no network fetch. + +## Files + +| File | Role | +|------|------| +| `PlayerViewController.swift` | Creates the `AVPlayer`, embeds an `AVPlayerViewController`, and attaches the SDK with `trackAvPlayerController`. | +| `HomeViewController.swift` | Landing list of sample streams. | +| `AppDelegate.swift` / `SceneDelegate.swift` | App entry point (programmatic window, no storyboard). | + +## Set your own workspace + stream + +Edit `PlayerViewController.swift`: + +```swift +// Replace with the Workspace Key from your FastPix dashboard. +private let workspaceKey = "1029207880411545601" +``` + +Replace `workspaceKey` (dashboard.fastpix.com → **Workspaces**), and edit the +`samples` list in `HomeViewController.swift` to play your own HLS URLs. Without a +valid key the app still plays video but shows an orange "Add your Workspace Key" +badge and sends no analytics. diff --git a/example/AVPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/example/AVPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 310f271..0000000 --- a/example/AVPlayerExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,24 +0,0 @@ -{ - "originHash" : "f7535a969251f0bf4f22ee3f35a7491cf4033d9a78d775655eee7b21ec52d054", - "pins" : [ - { - "identity" : "ios-core-data-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/FastPix/iOS-core-data-sdk.git", - "state" : { - "revision" : "1c349f3095ebd15d6fa221bc8f4d0456a95f7b1f", - "version" : "1.0.6" - } - }, - { - "identity" : "ios-data-avplayer-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/FastPix/iOS-data-avplayer-sdk.git", - "state" : { - "revision" : "1eab915f7140c90ec80dafd59313d304c6c9fbde", - "version" : "1.0.7" - } - } - ], - "version" : 3 -} diff --git a/example/AVPlayerExample.xcodeproj/project.xcworkspace/xcuserdata/d.nehareddy.xcuserdatad/UserInterfaceState.xcuserstate b/example/AVPlayerExample.xcodeproj/project.xcworkspace/xcuserdata/d.nehareddy.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 58456ed91c0c2b3b25193b815011503db95d0898..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24428 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 Mb$P$8U&FTkKQapE8~^|S diff --git a/example/AVPlayerExample.xcodeproj/xcuserdata/d.nehareddy.xcuserdatad/xcschemes/xcschememanagement.plist b/example/AVPlayerExample.xcodeproj/xcuserdata/d.nehareddy.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index c4dae4a..0000000 --- a/example/AVPlayerExample.xcodeproj/xcuserdata/d.nehareddy.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - SchemeUserState - - AVPlayerExample.xcscheme_^#shared#^_ - - orderHint - 0 - - - - diff --git a/example/README.md b/example/README.md deleted file mode 100644 index 4923c6c..0000000 --- a/example/README.md +++ /dev/null @@ -1,129 +0,0 @@ -# FastPix Video Data AVPlayer — Example App - -A minimal iOS (UIKit) app showing how to integrate the -[FastPix Video Data AVPlayer SDK](https://github.com/FastPix/iOS-data-avplayer-sdk) -to collect playback analytics from an `AVPlayerViewController`. - -## Requirements - -- macOS with **Xcode 15 or later** -- **iOS 14.0+** simulator or device -- A **FastPix Workspace Key** — grab it from the - [FastPix dashboard](https://dashboard.fastpix.com) under **Workspaces** -- (Device only) an Apple ID / signing team configured in Xcode - -## How to run this app - -### 1. Get the project - -If you cloned the SDK repo, the example lives in the `example/` folder: - -```bash -git clone https://github.com/FastPix/iOS-data-avplayer-sdk.git -cd iOS-data-avplayer-sdk/example -``` - -### 2. Open it in Xcode - -```bash -open AVPlayerExample.xcodeproj -``` - -Or launch Xcode → **File → Open…** → select `AVPlayerExample.xcodeproj`. - -### 3. Let Swift Package Manager resolve dependencies - -On first open, Xcode automatically fetches the packages -(`FastpixVideoDataAVPlayer` and its `FastpixiOSVideoDataCore` dependency). -Wait until the progress spinner in the toolbar finishes. If it doesn't start, -choose **File → Packages → Resolve Package Versions**. - -### 4. Add your Workspace Key - -Open `AVPlayerExample/PlayerViewController.swift` and replace the placeholder: - -```swift -// Replace with the Workspace Key from your FastPix dashboard. -private let workspaceKey = "YOUR_WORKSPACE_KEY" -``` - -Without a valid key the app still plays video, but the player screen shows an -orange **"Add your Workspace Key"** badge and no analytics are sent. -(Optionally edit the `samples` list in `HomeViewController.swift` to play your -own FastPix HLS URLs.) - -### 5. Pick a destination and run - -1. In the toolbar's scheme/destination selector, choose the **AVPlayerExample** - scheme and a target: - - **Simulator** — e.g. *iPhone 15 Pro* (no signing needed). - - **Physical device** — select **Signing & Capabilities**, pick your **Team** - so Xcode provisions the app, then plug in / select the device. -2. Press **⌘R** (or the ▶︎ Run button). -3. The app launches on the **Home** screen listing sample streams — tap one to - open the player. - -### 6. Verify tracking - -- On the player screen you should see a green **"FastPix tracking active"** - badge and the **Tracked metadata** card. -- Play, pause, and seek the video, then open your - [FastPix dashboard](https://dashboard.fastpix.com) — the view and its metrics - appear there (allow a short delay for data to arrive). - -### Troubleshooting - -- **"Failed to install" on a device** — clean with **Product → Clean Build - Folder (⇧⌘K)** and run again. On the first install, trust the developer - profile under **Settings → General → VPN & Device Management** on the device. -- **Package resolution errors** — **File → Packages → Reset Package Caches**, - then resolve again. -- **No data on the dashboard** — confirm `workspaceKey` is set correctly and the - device/simulator has network access. - -## App structure - -- `HomeViewController` — landing screen listing sample streams. -- `PlayerViewController` — plays the selected stream in an - `AVPlayerViewController` and attaches the SDK. Shows a small status panel - indicating whether tracking is active. - -## How the integration works - -```swift -import FastpixVideoDataAVPlayer - -// Keep a strong reference for the whole playback session. -let fpDataSDK = initAvPlayerTracking() - -// All metadata fields must live under the "data" key. -let customMetadata: [String: Any] = [ - "data": [ - "workspace_id": "YOUR_WORKSPACE_KEY", - "video_title": "Test Content", - "video_id": "sample-video-001", - "viewer_id": "user-12345", - "video_content_type": "movie", - "video_stream_type": "on-demand" - ] -] - -// Attach to however you present video: -fpDataSDK.trackAvPlayerController(playerController: playerVC, customMetadata: customMetadata) -// or trackAvPlayer(player:customMetadata:) -// or trackAvPlayerLayer(playerLayer:customMetadata:) -``` - -When you swap in a new video in the same player (playlists / "up next"), -notify the SDK so it starts a new view: - -```swift -fpDataSDK.dispatchEvent(event: "videoChange", metadata: [ - "video_id": "sample-video-002", - "video_title": "Next Content" -]) -``` - -See the -[official FastPix documentation](https://fastpix.com/docs/ios-and-cross-platform-players/monitor-avplayer) -for the full list of supported metadata and advanced usage.