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 58456ed..0000000 Binary files a/example/AVPlayerExample.xcodeproj/project.xcworkspace/xcuserdata/d.nehareddy.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ 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.