Skip to content

Commit

Permalink
Update metrics tracker to most recent specifications (#967)
Browse files Browse the repository at this point in the history
Co-authored-by: Walid Kayhal <[email protected]>
  • Loading branch information
defagos and waliid authored Aug 11, 2024
1 parent 827ac05 commit 3258b33
Show file tree
Hide file tree
Showing 51 changed files with 783 additions and 314 deletions.
25 changes: 20 additions & 5 deletions .swiftpm/xcode/xcshareddata/xcschemes/PillarboxMonitoring.xcscheme
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1600"
version = "1.7">
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
Expand All @@ -28,7 +27,23 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
language = "en"
region = "CH"
codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES"
testExecutionOrdering = "random">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "MonitoringTests"
BuildableName = "MonitoringTests"
BlueprintName = "MonitoringTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
Expand Down
2 changes: 1 addition & 1 deletion Demo/Sources/Metrics/DataVolumeChart.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ struct DataVolumeChart: View {
Chart(Array(metrics.suffix(limit).enumerated()), id: \.offset) { metrics in
BarMark(
x: .value("Index", metrics.offset),
y: .value("Data volume (\(Self.megabytes)", Double(metrics.element.increment.numberOfBytesTransferred) / 1_000_000),
y: .value("Data volume (\(Self.megabytes)", Double(metrics.element.increment.numberOfBytesTransferred) / 1024 * 1024),
width: .inset(1)
)
.foregroundStyle(.cyan)
Expand Down
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ let package = Package(
name: "MonitoringTests",
dependencies: [
.target(name: "PillarboxCircumspect"),
.target(name: "PillarboxMonitoring")
.target(name: "PillarboxMonitoring"),
.target(name: "PillarboxStreams")
]
),
.testTarget(
Expand Down
6 changes: 3 additions & 3 deletions Sources/Analytics/AnalyticsListener.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public enum AnalyticsListener {
///
/// - Parameter completion: A completion called when the listener has been started.
public static func start(completion: @escaping () -> Void) {
ComScoreInterceptor.start(completion: completion)
ComScoreHitInterceptor.start(completion: completion)
}

/// Captures comScore hits.
Expand All @@ -33,7 +33,7 @@ public enum AnalyticsListener {
/// which emits the associated hits.
public static func captureComScoreHits(perform: (AnyPublisher<ComScoreHit, Never>) -> Void) {
captureHits(perform: perform) { identifier in
ComScoreInterceptor.hitPublisher(for: identifier)
ComScoreHitInterceptor.publisher(for: identifier)
}
}

Expand All @@ -43,7 +43,7 @@ public enum AnalyticsListener {
/// which emits the associated hits.
public static func captureCommandersActHits(perform: (AnyPublisher<CommandersActHit, Never>) -> Void) {
captureHits(perform: perform) { identifier in
CommandersActInterceptor.hitPublisher(for: identifier)
CommandersActHitInterceptor.publisher(for: identifier)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ private enum ComScoreRequestInfoKey: String {
}

/// A tool that intercepts comScore requests and turns them into a hit stream.
enum ComScoreInterceptor {
enum ComScoreHitInterceptor {
private static var started = false
private static var cancellables = Set<AnyCancellable>()

Expand All @@ -40,7 +40,7 @@ enum ComScoreInterceptor {
.eraseToAnyPublisher()
}

static func hitPublisher(for identifier: String) -> AnyPublisher<ComScoreHit, Never> {
static func publisher(for identifier: String) -> AnyPublisher<ComScoreHit, Never> {
labelsPublisher()
.filter { $0.listener_session_id == identifier }
.compactMap { .init(from: $0) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import Foundation
import TCCore

/// A tool that intercepts Commanders Act requests and turns them into a hit stream.
enum CommandersActInterceptor {
static func hitPublisher(for identifier: String) -> AnyPublisher<CommandersActHit, Never> {
enum CommandersActHitInterceptor {
static func publisher(for identifier: String) -> AnyPublisher<CommandersActHit, Never> {
NotificationCenter.default.publisher(for: .init(rawValue: kTCNotification_HTTPRequest))
.compactMap { labels(from: $0) }
.filter { $0.listener_session_id == identifier }
Expand Down
32 changes: 17 additions & 15 deletions Sources/Analytics/CommandersAct/CommandersActService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,13 @@ final class CommandersActService {
private var serverSide: ServerSide?
private var vendor: Vendor?

private static func device() -> String {
guard !ProcessInfo.processInfo.isRunningOnMac else { return "desktop" }
switch UIDevice.current.userInterfaceIdiom {
case .phone:
return "phone"
case .pad:
return "tablet"
case .tv:
return "tvbox"
default:
return "phone"
}
}

func start(with configuration: Analytics.Configuration) {
vendor = configuration.vendor

if let serverSide = ServerSide(siteID: 3666, andSourceKey: configuration.sourceKey) {
serverSide.addPermanentData("app_library_version", withValue: Analytics.version)
serverSide.addPermanentData("navigation_app_site_name", withValue: configuration.appSiteName)
serverSide.addPermanentData("navigation_device", withValue: Self.device())
serverSide.addPermanentData("navigation_device", withValue: Self.device)
serverSide.enableRunningInBackground()
serverSide.waitForUserAgent()
self.serverSide = serverSide
Expand Down Expand Up @@ -67,6 +53,22 @@ final class CommandersActService {
}
}

extension CommandersActService {
private static var device: String = {
guard !ProcessInfo.processInfo.isRunningOnMac else { return "desktop" }
switch UIDevice.current.userInterfaceIdiom {
case .phone:
return "phone"
case .pad:
return "tablet"
case .tv:
return "tvbox"
default:
return "phone"
}
}()
}

extension TCAdditionalProperties {
func addNonBlankAdditionalProperty(_ key: String, withStringValue value: String?) {
guard let value, !value.isBlank else { return }
Expand Down
25 changes: 25 additions & 0 deletions Sources/Monitoring/Capture/MetricHitInterceptor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// Copyright (c) SRG SSR. All rights reserved.
//
// License information is available from the LICENSE file.
//

import Combine
import Foundation

/// A tool that intercepts metric requests and turns them into a hit stream.
enum MetricHitInterceptor {
static func publisher(for identifier: String) -> AnyPublisher<Encodable, Never> {
NotificationCenter.default.publisher(for: .didSendMetricRequest)
.compactMap { payload(from: $0, for: identifier) }
.eraseToAnyPublisher()
}

private static func payload(from notification: Notification, for identifier: String) -> Encodable? {
guard let userInfo = notification.userInfo,
let requestIdentifier = userInfo[MetricRequestInfoKey.identifier] as? String, requestIdentifier == identifier else {
return nil
}
return userInfo[MetricRequestInfoKey.payload] as? Encodable
}
}
38 changes: 38 additions & 0 deletions Sources/Monitoring/Capture/MetricHitListener.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// Copyright (c) SRG SSR. All rights reserved.
//
// License information is available from the LICENSE file.
//

import Combine
import Foundation

enum MetricHitListener {
private static var sessionIdentifier: String?

static func captureMetricHits(perform: (AnyPublisher<Encodable, Never>) -> Void) {
captureHits(perform: perform) { identifier in
MetricHitInterceptor.publisher(for: identifier)
}
}

private static func captureHits<P>(perform: (P) -> Void, using publisher: (String) -> P) where P: Publisher, P.Failure == Never {
assert(sessionIdentifier == nil, "Multiple captures are not supported")

let identifier = UUID().uuidString
sessionIdentifier = identifier
defer {
sessionIdentifier = nil
}

perform(publisher(identifier))
}

static func capture(_ payload: Encodable) {
guard let sessionIdentifier else { return }
NotificationCenter.default.post(name: .didSendMetricRequest, object: self, userInfo: [
MetricRequestInfoKey.identifier: sessionIdentifier,
MetricRequestInfoKey.payload: payload
])
}
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,4 @@ extension Double {
var toMilliseconds: Int {
Int((self * 1000).rounded())
}

var toBytes: Int {
Int((self / 8).rounded())
}
}
14 changes: 14 additions & 0 deletions Sources/Monitoring/Extensions/PlayerProperties.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Copyright (c) SRG SSR. All rights reserved.
//
// License information is available from the LICENSE file.
//

import CoreMedia
import PillarboxPlayer

extension PlayerProperties {
func endOffset() -> CMTime {
max(seekableTimeRange.end - time(), .zero)
}
}
File renamed without changes.
20 changes: 0 additions & 20 deletions Sources/Monitoring/MetricPayload.swift

This file was deleted.

Loading

0 comments on commit 3258b33

Please sign in to comment.