diff --git a/Sources/Analytics/ApplicationState.swift b/Sources/Analytics/ApplicationState.swift new file mode 100644 index 000000000..93250642c --- /dev/null +++ b/Sources/Analytics/ApplicationState.swift @@ -0,0 +1,12 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import Foundation + +enum ApplicationState { + case foreground + case background +} diff --git a/Sources/Analytics/ComScore/ComScoreService.swift b/Sources/Analytics/ComScore/ComScoreService.swift index e2aaa6e3f..ce92ddb81 100644 --- a/Sources/Analytics/ComScore/ComScoreService.swift +++ b/Sources/Analytics/ComScore/ComScoreService.swift @@ -26,7 +26,6 @@ struct ComScoreService { comScoreConfiguration.addClient(with: publisherConfiguration) comScoreConfiguration.applicationVersion = applicationVersion - comScoreConfiguration.usagePropertiesAutoUpdateMode = .foregroundAndBackground comScoreConfiguration.preventAdSupportUsage = true comScoreConfiguration.addPersistentLabels([ "mp_brand": configuration.vendor.rawValue, diff --git a/Sources/Analytics/ComScore/ComScoreTracker.swift b/Sources/Analytics/ComScore/ComScoreTracker.swift index abd22faef..00179bf2f 100644 --- a/Sources/Analytics/ComScore/ComScoreTracker.swift +++ b/Sources/Analytics/ComScore/ComScoreTracker.swift @@ -9,6 +9,7 @@ import ComScore import CoreMedia import Foundation import Player +import UIKit /// A comScore tracker for streaming. /// @@ -34,12 +35,17 @@ public final class ComScoreTracker: PlayerItemTracker { } .store(in: &cancellables) - Publishers.CombineLatest3(player.$playbackState, player.$isSeeking, player.$isBuffering) - .weakCapture(player) - .sink { [weak self] state, player in - self?.notify(playbackState: state.0, isSeeking: state.1, isBuffering: state.2, player: player) - } - .store(in: &cancellables) + Publishers.CombineLatest4( + UIApplication.shared.applicationStatePublisher(), + player.$playbackState, + player.$isSeeking, + player.$isBuffering + ) + .weakCapture(player) + .sink { [weak self] state, player in + self?.notify(applicationState: state.0, playbackState: state.1, isSeeking: state.2, isBuffering: state.3, player: player) + } + .store(in: &cancellables) player.objectWillChange .receive(on: DispatchQueue.main) @@ -59,12 +65,18 @@ public final class ComScoreTracker: PlayerItemTracker { streamingAnalytics = SCORStreamingAnalytics() } - private func notify(playbackState: PlaybackState, isSeeking: Bool, isBuffering: Bool, player: Player) { + // swiftlint:disable:next cyclomatic_complexity + private func notify(applicationState: ApplicationState, playbackState: PlaybackState, isSeeking: Bool, isBuffering: Bool, player: Player) { guard !metadata.labels.isEmpty else { return } AnalyticsListener.capture(streamingAnalytics.configuration()) streamingAnalytics.setProperties(for: player, streamType: metadata.streamType) + guard applicationState == .foreground else { + streamingAnalytics.notifyEvent(for: .paused, at: player.effectivePlaybackSpeed) + return + } + switch (isSeeking, isBuffering) { case (true, true): streamingAnalytics.notifySeekStart() diff --git a/Sources/Analytics/UIApplication.swift b/Sources/Analytics/UIApplication.swift new file mode 100644 index 000000000..9a02de002 --- /dev/null +++ b/Sources/Analytics/UIApplication.swift @@ -0,0 +1,23 @@ +// +// Copyright (c) SRG SSR. All rights reserved. +// +// License information is available from the LICENSE file. +// + +import Combine +import Core +import UIKit + +extension UIApplication { + func applicationStatePublisher() -> AnyPublisher { + Publishers.Merge( + NotificationCenter.default.weakPublisher(for: UIApplication.didEnterBackgroundNotification, object: self) + .map { _ in .background }, + NotificationCenter.default.weakPublisher(for: UIApplication.didBecomeActiveNotification, object: self) + .map { _ in .foreground } + ) + .prepend(applicationState == .background ? .background : .foreground) + .removeDuplicates() + .eraseToAnyPublisher() + } +}