diff --git a/.travis.yml b/.travis.yml index a16d6a5..e1136c2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,20 +8,19 @@ git: install: - bundle install - - brew update-reset && brew update - brew install sourcery - carthage update --platform iOS matrix: include: - os: osx - osx_image: xcode10.1 + osx_image: xcode10.2 before_script: - ". ./fastlane/travis-scripts/pre-xcode10.sh" script: - bundle exec fastlane test - bundle exec fastlane build_tvos_target - - bundle exec fastlane build_tutorials + #- bundle exec fastlane build_tutorials - bundle exec fastlane lint_current_podspec - bundle exec danger || true diff --git a/PlayerControls b/PlayerControls index cf783a3..736e4bd 160000 --- a/PlayerControls +++ b/PlayerControls @@ -1 +1 @@ -Subproject commit cf783a3dcf44a879bb7121374ec3c032eb10b1af +Subproject commit 736e4bd24e0dbbdf7cb3d1bff71e856f1fd0bde7 diff --git a/PlayerCore b/PlayerCore index d3d6a25..2bf9795 160000 --- a/PlayerCore +++ b/PlayerCore @@ -1 +1 @@ -Subproject commit d3d6a25e1907ca9601221c3ecea222281b0e7c8c +Subproject commit 2bf9795f5a4826bd6faecd178457b30cb9eebeed diff --git a/VerizonVideoPartnerSDK.xcodeproj/project.pbxproj b/VerizonVideoPartnerSDK.xcodeproj/project.pbxproj index db0babd..bbf537d 100644 --- a/VerizonVideoPartnerSDK.xcodeproj/project.pbxproj +++ b/VerizonVideoPartnerSDK.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 06123FDC225E42DE00CA8766 /* Pipeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEBE4A7E1CA2EEDC006E01C1 /* Pipeline.swift */; }; 061AFD6521F256CB0024BAC5 /* ContentPlaybackCycleDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 061AFD6321F256CA0024BAC5 /* ContentPlaybackCycleDetector.swift */; }; 061AFD6721F256ED0024BAC5 /* ContentPlaybackCycleDetectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 061AFD6421F256CA0024BAC5 /* ContentPlaybackCycleDetectorTests.swift */; }; 061AFD7421F53BC30024BAC5 /* ContentPlaybackCycleDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 061AFD6321F256CA0024BAC5 /* ContentPlaybackCycleDetector.swift */; }; @@ -227,8 +228,6 @@ DE684CD71E0C2F36007CE35C /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE684CD61E0C2F36007CE35C /* Action.swift */; }; DE684CD81E0C2F36007CE35C /* Action.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE684CD61E0C2F36007CE35C /* Action.swift */; }; DE684CDC1E0EBFC3007CE35C /* SystemPlayerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5067F5FE1D49120E00FEC27C /* SystemPlayerViewController.swift */; }; - DE79CE211E979F9700C3A660 /* AdURLProviderProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE79CE201E979F9700C3A660 /* AdURLProviderProcess.swift */; }; - DE79CE221E979F9700C3A660 /* AdURLProviderProcess.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE79CE201E979F9700C3A660 /* AdURLProviderProcess.swift */; }; DE8416A11F4493FF00E57971 /* PlayerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8416A01F4493FF00E57971 /* PlayerInterface.swift */; }; DE8416A21F4493FF00E57971 /* PlayerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = DE8416A01F4493FF00E57971 /* PlayerInterface.swift */; }; DEAE91D81CAA830800C736D0 /* ApplyDecorator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEAE91D71CAA830800C736D0 /* ApplyDecorator.swift */; }; @@ -609,7 +608,6 @@ DE4C51E01C80D43200BFFB0B /* TrackingPixelsConnector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackingPixelsConnector.swift; sourceTree = ""; }; DE611A791E9D40F200D253B2 /* PlayerControls.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = PlayerControls.xcodeproj; path = PlayerControls/PlayerControls/PlayerControls.xcodeproj; sourceTree = ""; }; DE684CD61E0C2F36007CE35C /* Action.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Action.swift; sourceTree = ""; }; - DE79CE201E979F9700C3A660 /* AdURLProviderProcess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AdURLProviderProcess.swift; sourceTree = ""; }; DE8416A01F4493FF00E57971 /* PlayerInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlayerInterface.swift; sourceTree = ""; }; DEAE91D71CAA830800C736D0 /* ApplyDecorator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApplyDecorator.swift; sourceTree = ""; }; DEAE91DA1CAA83D200C736D0 /* SkipRepeatsDecorator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SkipRepeatsDecorator.swift; sourceTree = ""; }; @@ -1117,7 +1115,6 @@ 06F8DBB621C2A2A00019021D /* VRM New Core */, DE1685D71CE7308500492A90 /* vast parser */, 50D7476D1CE6234900CB91D4 /* vrm */, - DE79CE201E979F9700C3A660 /* AdURLProviderProcess.swift */, E23168002126D67D00F5AD5B /* AdStartTimeoutController.swift */, E231AA78213402C3008C5B6A /* AdStartTimeoutControllerTest.swift */, E2316874212C3B5D00F5AD5B /* MaxShowTimeController.swift */, @@ -1556,11 +1553,11 @@ }; buildConfigurationList = DEF3CBB11C7F12D200CB5A74 /* Build configuration list for PBXProject "VerizonVideoPartnerSDK" */; compatibilityVersion = "Xcode 6.3"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( - English, en, + Base, ); mainGroup = DEF3CBAD1C7F12D200CB5A74; productRefGroup = DEF3CBAD1C7F12D200CB5A74; @@ -1804,7 +1801,6 @@ 50A11DAB1D59D7E000F4A068 /* Network.swift in Sources */, E22B6ADC21F0A95700D480A0 /* VVPSDK_Version.swift in Sources */, 50C99D4F2010B4E00041B013 /* Observable.swift in Sources */, - DE79CE221E979F9700C3A660 /* AdURLProviderProcess.swift in Sources */, DEE069D61E840BDF00A95BE9 /* VideoProviderResponse.swift in Sources */, DEB112371E30F0CE007E65BD /* Context.swift in Sources */, E22B6AE621F0C6D100D480A0 /* VRMTimeoutController.swift in Sources */, @@ -2006,7 +2002,6 @@ 067209BE21B6BD9A0086CDBE /* OpenMeasurementContext.swift in Sources */, 5034AD2A207E69CA00B5CAA1 /* VideoSelector.swift in Sources */, 50D442591D0FF9DB00C0DAB0 /* AdMetrics.swift in Sources */, - DE79CE211E979F9700C3A660 /* AdURLProviderProcess.swift in Sources */, 062E9B3E20F5097C00AF1D54 /* PlayerViewController_Clickthrough.swift in Sources */, E23168A5212C4C9E00F5AD5B /* MaxShowTimeController.swift in Sources */, ); @@ -2047,6 +2042,7 @@ FEEB8E5B1F4C92F5003507BE /* ErrorDetectorTests.swift in Sources */, DEB112331E30E5ED007E65BD /* VideoLoadingDetectorTests.swift in Sources */, 06BF5394209B471A00B98A6D /* UserActionsDetectorTests.swift in Sources */, + 06123FDC225E42DE00CA8766 /* Pipeline.swift in Sources */, E287B0A621F8A67F00FC49AA /* VRMSelectFinalResultControllerTest.swift in Sources */, 508748261C80750100F2511A /* Recorder.swift in Sources */, 06EC86EF2214842900CB4A8E /* AdVASTProgressDetectorTest.swift in Sources */, diff --git a/VerizonVideoPartnerSDK.xcodeproj/xcshareddata/xcschemes/VerizonVideoPartnerSDKTests.xcscheme b/VerizonVideoPartnerSDK.xcodeproj/xcshareddata/xcschemes/VerizonVideoPartnerSDKTests.xcscheme index eb89310..3bd8d9c 100644 --- a/VerizonVideoPartnerSDK.xcodeproj/xcshareddata/xcschemes/VerizonVideoPartnerSDKTests.xcscheme +++ b/VerizonVideoPartnerSDK.xcodeproj/xcshareddata/xcschemes/VerizonVideoPartnerSDKTests.xcscheme @@ -1,6 +1,6 @@ 10') + xcversion(version: '~> 10.2') end lane :clean do diff --git a/fastlane/travis-scripts/pre-xcode10.sh b/fastlane/travis-scripts/pre-xcode10.sh index ffb591a..e36b3e3 100644 --- a/fastlane/travis-scripts/pre-xcode10.sh +++ b/fastlane/travis-scripts/pre-xcode10.sh @@ -1,5 +1,5 @@ #!/bin/bash set -e -export IOS_SIMULATOR_UDID=`instruments -s devices | grep "iPhone 8 (12.0" | awk -F '[ ]' '{print $4}' | awk -F '[\[]' '{print $2}' | sed 's/.$//'` +export IOS_SIMULATOR_UDID=`instruments -s devices | grep "iPhone 8 (12.2" | awk -F '[ ]' '{print $4}' | awk -F '[\[]' '{print $2}' | sed 's/.$//'` xcrun simctl boot $IOS_SIMULATOR_UDID diff --git a/sources/VVPSDK.swift b/sources/VVPSDK.swift index a2d7f36..22588a5 100644 --- a/sources/VVPSDK.swift +++ b/sources/VVPSDK.swift @@ -50,19 +50,6 @@ public struct VVPSDK { self.vrmProvider = VRMProvider(session: ephemeralSession) } - /* attach ad url process listener */ do { - if let url = configuration.telemetry?.url { - let listener = Telemetry.Listeners.AdURLProcessListener.shared - - listener.session = ephemeralSession - listener.url = url - - Telemetry.Station.shared.add( - listener: Telemetry.Listeners.AdURLProcessListener.shared - ) - } - } - telemetryMetrics = configuration .telemetry .flatMap { config in diff --git a/sources/advertisements/AdURLProviderProcess.swift b/sources/advertisements/AdURLProviderProcess.swift deleted file mode 100644 index f9b5929..0000000 --- a/sources/advertisements/AdURLProviderProcess.swift +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright 2018, Oath Inc. -// Licensed under the terms of the MIT License. See LICENSE.md file in project root for terms. -import Foundation -import PlayerCore - -extension Telemetry.Listeners { - public final class AdURLProcessListener: TelemetryStationListener { - public static let shared = AdURLProcessListener() - - public var session: URLSession? - public var url: URL? - - public func process(event: Telemetry.Event, in context: Telemetry.Context, at time: Date) { - guard context.isType(type: AdURLProviderProcess.self) else { return } - guard case let Telemetry.Event.Log(json) = event else { return } - guard let url = url, let session = session else { return } - - var request = URLRequest(url: url) - request.httpMethod = "POST" - request.httpBody = try! JSONSerialization.data(withJSONObject: json) - request.setValue("application/json", forHTTPHeaderField: "Content-Type") - - session.dataTask(with: request).resume() - } - } -} - -final class AdURLProviderProcess { - var steps: [JSON] = [] - private var telemetry: Telemetry.Channel! = nil - - private func append(json: JSON) { - var json = json - json["date"] = String(describing: Date()) - steps.append(json) - } - - private var isFired = false - func unimplementedHandler(argumnet: Any) { - guard !isFired else { return } - isFired = true - - telemetry.log(json: ["steps": steps]) - } - - init(for url: URL) { - append(json: ["url": url.absoluteString]) - telemetry = Telemetry.Station.shared.makeChannel(for: self) - } - - func startRequest() { - append(json: ["step": "start"]) - } - - func requestGroups() { - append(json: ["step": "request groups"]) - } - - private func descriptionFor(item: VRMProvider.Item) -> JSON { - switch item { - case let .vast(vast, meta): - return [ - "vast": vast, - "meta": [ - "engine type": meta.engineType, - "rule id": meta.ruleId, - "rule company id": meta.ruleCompanyId, - "venor": meta.vendor, - "name": meta.name - ] - ] - - case let .url(url, meta): - return [ - "url": url.absoluteString, - "meta": [ - "engine type": meta.engineType, - "rule id": meta.ruleId, - "rule company id": meta.ruleCompanyId, - "venor": meta.vendor, - "name": meta.name - ] - ] - } - } - - private func descriptionFor(result: PlayerCore.Ad.VASTModel) -> JSON { - return [ - "media file": result.mp4MediaFiles.first?.url.absoluteString ?? result.vpaidMediaFiles.first?.url.absoluteString ?? NSNull(), - "click": result.clickthrough?.absoluteString ?? NSNull(), - "id": result.id ?? NSNull() - ] - } - - func didReceiveGroups(_ groups: [[VRMProvider.Item]]) { - let groupsDescription = groups.map { group in - group.map(descriptionFor(item:)) - } - - append(json: ["step": "did receive groups", - "groups": groupsDescription ]) - } - - func didFailToReceiveGroups() { - append(json: ["step": "did fail receive groups"]) - } - - func processItem(_ item: VRMProvider.Item) { - append(json: ["step": "process item", - "item": descriptionFor(item:item)]) - } - - func didProcessItem(_ item: VRMProvider.Item, to result: PlayerCore.Ad.VASTModel) { - append(json: ["step": "process item finished", - "item": descriptionFor(item: item), - "result": descriptionFor(result: result)]) - } - - func didFailToProcessItem(_ item: VRMProvider.Item) { - append(json: ["step": "process item failed", - "item": descriptionFor(item: item)]) - } - - func hitSoftTimeout() { - append(json: ["step": "soft timeout hit"]) - } - - func hitHardTimeout() { - append(json: ["step": "hard timeout hit"]) - } - - func didStop() { - append(json: ["step": "stop ongoing tasks"]) - } - - func didRetrieveResult(_ result: PlayerCore.Ad.VASTModel) { - append(json: ["step": "finished", - "result": descriptionFor(result: result)]) - } - - func didFailToRetrieveResult() { - append(json: ["step": "failed"]) - } -} diff --git a/sources/dynamic analytics/PropsMapping.swift b/sources/dynamic analytics/PropsMapping.swift index 684e828..b274683 100644 --- a/sources/dynamic analytics/PropsMapping.swift +++ b/sources/dynamic analytics/PropsMapping.swift @@ -6,7 +6,7 @@ import JavaScriptCore import CoreMedia import PlayerCore -indirect enum JSøN { +indirect enum JSøN: Hashable { case null case bool(Bool) case string(String) diff --git a/sources/telemetry/Metrics.swift b/sources/telemetry/Metrics.swift index 960cc74..0bdb2e3 100644 --- a/sources/telemetry/Metrics.swift +++ b/sources/telemetry/Metrics.swift @@ -3,8 +3,13 @@ import Foundation import PlayerCore import CoreMedia - extension Telemetry { + + struct TelemetryJSON { + let context: JSON + let data: JSøN + } + struct Metrics { let airPlay: AirPlay let subtitles: Subtitles @@ -50,10 +55,10 @@ extension Telemetry { extension Telemetry.Metrics { final class AirPlay { let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () private var isTriggered = false - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { self.context = context self.send = send } @@ -64,7 +69,7 @@ extension Telemetry.Metrics { guard item.ad.airPlay == .active || item.content.airPlay == .active else { return } isTriggered = true - send(telemetryJSON(withContext: context, type: "EXTERNAL_PLAYBACK_TRIGGERED")) + send(telemetryJSøN(withContext: context, type: "EXTERNAL_PLAYBACK_TRIGGERED")) } } } @@ -72,10 +77,10 @@ extension Telemetry.Metrics { extension Telemetry.Metrics { final class Subtitles { let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () private var triggeredSessionIds: Set = [] - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { self.context = context self.send = send } @@ -104,9 +109,9 @@ extension Telemetry.Metrics { guard let selectedSubtitlesType = selectedSubtitlesType(item: item) else { return } triggeredSessionIds.insert(props.session.playback.id) - send(telemetryJSON(withContext: context, + send(telemetryJSøN(withContext: context, type: "SUBTITLES_ENABLED", - value: ["subtitlesType" : selectedSubtitlesType])) + value: ["subtitlesType" : selectedSubtitlesType |> json] |> json)) } } } @@ -114,15 +119,15 @@ extension Telemetry.Metrics { extension Telemetry.Metrics { struct VideoProvider { let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () func process(error: Error) { guard let error = error as? Network.Parse.JSONError else { return } - send(telemetryJSON(withContext: context, + send(telemetryJSøN(withContext: context, type: "VIDEO_SERVICE_JSON_PARSING_ERROR", - value: ["responseJson": error.json, - "message": error.message])) + value: ["responseJSON": error.json |> json, + "message": error.message |> json] |> json)) } } } @@ -130,11 +135,11 @@ extension Telemetry.Metrics { extension Telemetry.Metrics { final class PictureInPicture { let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () private var triggeredSessionIds: Set = [] - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { self.context = context self.send = send } @@ -145,7 +150,7 @@ extension Telemetry.Metrics { guard item.content.pictureInPictureMode == .active else { return } triggeredSessionIds.insert(props.session.playback.id) - send(telemetryJSON(withContext: context, type: "PICTURE_IN_PICTURE_MODE_ENABLED")) + send(telemetryJSøN(withContext: context, type: "PICTURE_IN_PICTURE_MODE_ENABLED")) } } } @@ -153,11 +158,11 @@ extension Telemetry.Metrics { extension Telemetry.Metrics { final class AdStartTimeout { let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () private var processedAds = Set() - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { self.context = context self.send = send } @@ -171,9 +176,9 @@ extension Telemetry.Metrics { guard isTimeoutReached, !processedAds.contains(ruleId) else { return } processedAds.insert(ruleId) - send(telemetryJSON(withContext: context, + send(telemetryJSøN(withContext: context, type: "START_TIMEOUT_REACHED", - value: ["rid": ruleId])) + value: ["rid": ruleId |> json] |> json)) } } } @@ -184,11 +189,11 @@ extension Telemetry.Metrics { final class Reporter { let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () private var uniqueKeys = Set() - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { self.context = context self.send = send } @@ -202,9 +207,9 @@ extension Telemetry.Metrics { uniqueKeys.insert(uniqueKey) let timeInterval = Int(finishAt.timeIntervalSince(startAt) * 1000) - send(telemetryJSON(withContext: context, + send(telemetryJSøN(withContext: context, type: telemetryType, - value: ["time": timeInterval])) + value: ["time": timeInterval |> json] |> json)) } } @@ -212,7 +217,7 @@ extension Telemetry.Metrics { let mp4Ad: MP4Ad let content: Content - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { mp4Ad = MP4Ad(context: context, send: send) vrm = VRM(context: context, send: send) content = Content(context: context, send: send) @@ -235,7 +240,7 @@ extension Telemetry.Metrics.Buffering { struct MP4Ad { let mp4Ad: Reporter - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { mp4Ad = Reporter(context: context, send: send) } @@ -251,7 +256,7 @@ extension Telemetry.Metrics.Buffering { struct VRM { let vrm: Reporter - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { vrm = Reporter(context: context, send: send) } @@ -267,7 +272,7 @@ extension Telemetry.Metrics.Buffering { struct Content { let content: Reporter - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { content = Reporter(context: context, send: send) } @@ -287,7 +292,7 @@ extension Telemetry.Metrics { let javascriptErrorReporter: JSEvaluationErrorReporter let unsupportedVPAIDReporter: UnsupportedVPAIDReporter - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { abuseEventReporter = AbuseEventErrorReporter(context: context, send: send) javascriptErrorReporter = JSEvaluationErrorReporter(context: context, send: send) unsupportedVPAIDReporter = UnsupportedVPAIDReporter(context: context, send: send) @@ -314,11 +319,11 @@ extension Telemetry.Metrics.VPAID { } let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () private var processedAbuseErrors = Set() - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { self.context = context self.send = send } @@ -330,10 +335,10 @@ extension Telemetry.Metrics.VPAID { guard !processedAbuseErrors.contains(candidate) else { return } processedAbuseErrors.insert(candidate) - send(telemetryJSON(withContext: context, + send(telemetryJSøN(withContext: context, type: "VPAID_UNIQUE_EVENT_ABUSE", - value: ["event_name": error.eventName, - "rid": ruleId])) + value: ["event_name": error.eventName |> json, + "rid": ruleId |> json] |> json)) } } } @@ -347,33 +352,37 @@ extension Telemetry.Metrics.VPAID { } let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () private var processedJsErrors = Set() - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { self.context = context self.send = send } func process(javascriptErrors: [Error], forRuleId ruleId: String?) { guard let ruleId = ruleId else { return } - func prepareValue(from jsError: Error, with ruleId: String?) -> JSON { - var value: JSON = ["rid": ruleId] - - if let error = jsError as NSError? { - let userInfo = error.userInfo.reduce([:]) { (result, next) -> JSON in - var newResult = result - newResult["\(next.key)"] = "\(next.value)" - return newResult + func prepareValue(from jsError: Error, with ruleId: String) -> JSøN { + let value: JSøN = { + if let error = jsError as NSError? { + let ruleIdJson = ruleId |> json + let errorCodeJson = "\(error.code)" |> json + let errorDescriptionJson = error.description |> json + let errorUserInfoJson = error.userInfo.description |> json + return [ + "rid" : ruleIdJson, + "error_code" : errorCodeJson, + "error_description" : errorDescriptionJson, + "error_additional_info": errorUserInfoJson + ] |> json + } else { + return [ + "rid" : ruleId |> json, + "error_description" : jsError.localizedDescription |> json + ] |> json } - value["error_code"] = "\(error.code)" - value["error_description"] = error.description - value["error_additional_info"] = userInfo - } else { - value["error_description"] = jsError.localizedDescription - } - + }() return value } @@ -382,7 +391,7 @@ extension Telemetry.Metrics.VPAID { guard !processedJsErrors.contains(candidate) else { return } processedJsErrors.insert(candidate) - send(telemetryJSON(withContext: context, + send(telemetryJSøN(withContext: context, type: "VPAID_JAVASCRIPT_EVALUATION_ERROR", value: prepareValue(from: jserror, with: ruleId))) @@ -394,11 +403,11 @@ extension Telemetry.Metrics.VPAID { extension Telemetry.Metrics.VPAID { final class UnsupportedVPAIDReporter { let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () private var processedAds = Set() - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { self.context = context self.send = send } @@ -409,9 +418,9 @@ extension Telemetry.Metrics.VPAID { !processedAds.contains(ruleId) else { return } processedAds.insert(ruleId) - send(telemetryJSON(withContext: context, + send(telemetryJSøN(withContext: context, type: "VPAID_UNSUPPORTED_VERSION_ERROR", - value: ["rid": ruleId])) + value: ["rid": ruleId |> json] |> json)) } } } @@ -423,7 +432,7 @@ extension Telemetry.Metrics { let failedConfigurationReporter: FailedConfigurationReporter let scriptFetchingFailedReporter: ScriptFetchingFailedReporter - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { successInitializationReporter = SuccessInitializationReporter(context: context, send: send) failedConfigurationReporter = FailedConfigurationReporter(context: context, send: send) scriptFetchingFailedReporter = ScriptFetchingFailedReporter(context: context, send: send) @@ -453,11 +462,11 @@ extension Telemetry.Metrics { extension Telemetry.Metrics.OpenMeasurement { final class SuccessInitializationReporter { let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () private var processedAds = Set() - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { self.context = context self.send = send } @@ -468,9 +477,9 @@ extension Telemetry.Metrics.OpenMeasurement { !processedAds.contains(ruleId) else { return } processedAds.insert(ruleId) - send(telemetryJSON(withContext: context, + send(telemetryJSøN(withContext: context, type: "OM_SDK_INITIATED", - value: ["rid": ruleId])) + value: ["rid": ruleId |> json] |> json)) } } } @@ -478,11 +487,11 @@ extension Telemetry.Metrics.OpenMeasurement { extension Telemetry.Metrics.OpenMeasurement { final class FailedConfigurationReporter { let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () private var processedAds = Set() - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { self.context = context self.send = send } @@ -493,21 +502,21 @@ extension Telemetry.Metrics.OpenMeasurement { !processedAds.contains(ruleId) else { return } processedAds.insert(ruleId) - send(telemetryJSON(withContext: context, + send(telemetryJSøN(withContext: context, type: "OM_SDK_ERROR", - value: ["message": error.localizedDescription, - "rid": ruleId])) + value: ["message": error.localizedDescription |> json, + "rid": ruleId |> json] |> json)) } } } extension Telemetry.Metrics.OpenMeasurement { final class ScriptFetchingFailedReporter { let context: JSON - let send: (JSON) -> () + let send: (Telemetry.TelemetryJSON) -> () private var processed = false - init(context: JSON, send: @escaping (JSON) -> ()) { + init(context: JSON, send: @escaping (Telemetry.TelemetryJSON) -> ()) { self.context = context self.send = send } @@ -516,19 +525,19 @@ extension Telemetry.Metrics.OpenMeasurement { guard let error = error, processed == false else { return } processed = true - send(telemetryJSON(withContext: context, + send(telemetryJSøN(withContext: context, type: "OM_SDK_SCRIPT_FETCHING_FAILED", - value: ["message": error.localizedDescription])) + value: ["message": error.localizedDescription |> json] |> json)) } } } -func telemetryJSON(withContext context: JSON, type: String, value: JSON = [:]) -> JSON { - return [ - "context" : context, +func telemetryJSøN(withContext context: JSON, type: String, value: JSøN = .null) -> Telemetry.TelemetryJSON { + let data: JSøN = [ "data" : [ - "type" : type, + "type" : type |> json, "value": value - ] - ] + ] |> json + ] |> json + return Telemetry.TelemetryJSON(context: context, data: data) } diff --git a/sources/telemetry/Telemetry.swift b/sources/telemetry/Telemetry.swift index 172f114..35eac36 100644 --- a/sources/telemetry/Telemetry.swift +++ b/sources/telemetry/Telemetry.swift @@ -95,11 +95,15 @@ public enum Telemetry { let session: URLSession let url: URL - func send(json: JSON) { + func send(json: Telemetry.TelemetryJSON) { + let telemetryJSON = [ + "context" : json.context, + "data" : json.data.object + ] var request = URLRequest(url: url) request.httpMethod = "POST" request.httpBody = try? JSONSerialization.data( - withJSONObject: json, + withJSONObject: telemetryJSON, options: .prettyPrinted) request.setValue("application/json", forHTTPHeaderField: "Content-Type") session.dataTask(with: request).resume() diff --git a/sources/telemetry/TelemetryMetricsTest.swift b/sources/telemetry/TelemetryMetricsTest.swift index babe5ad..bae7160 100644 --- a/sources/telemetry/TelemetryMetricsTest.swift +++ b/sources/telemetry/TelemetryMetricsTest.swift @@ -34,13 +34,8 @@ class TelemetryMetricsTest: XCTestCase { override func setUp() { super.setUp() - func compareJSON(target: JSON, recorded: JSON) -> Bool { - let targetJSONkeys = target.keys - let recordedJSONkeys = recorded.keys - guard targetJSONkeys.elementsEqual(recordedJSONkeys) else { return false } - let targetDictionary = target as NSDictionary - let recordedDictionary = recorded as NSDictionary - return targetDictionary.isEqual(recordedDictionary) + func compareJSON(target: Telemetry.TelemetryJSON, recorded: Telemetry.TelemetryJSON) -> Bool { + return target.data == recorded.data } let send = recorder.hook("hook", cmp: compareJSON) @@ -69,9 +64,9 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - abuseTelemetry.send(json(for: "VPAID_UNIQUE_EVENT_ABUSE", - and: ["event_name": "AdImpression", - "rid": "rule id"])) + abuseTelemetry.send(telemetryJSON(for: "VPAID_UNIQUE_EVENT_ABUSE", + and: ["event_name": "AdImpression" |> json, + "rid": "rule id" |> json])) } } @@ -85,12 +80,12 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - abuseTelemetry.send(json(for: "VPAID_UNIQUE_EVENT_ABUSE", - and: ["event_name": "AdImpression", - "rid": "rule id"])) - abuseTelemetry.send(json(for: "VPAID_UNIQUE_EVENT_ABUSE", - and: ["event_name": "AdStart", - "rid": "rule id"])) + abuseTelemetry.send(telemetryJSON(for: "VPAID_UNIQUE_EVENT_ABUSE", + and: ["event_name": "AdImpression" |> json, + "rid": "rule id" |> json])) + abuseTelemetry.send(telemetryJSON(for: "VPAID_UNIQUE_EVENT_ABUSE", + and: ["event_name": "AdStart" |> json, + "rid": "rule id" |> json])) } } @@ -103,12 +98,12 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - abuseTelemetry.send(json(for: "VPAID_UNIQUE_EVENT_ABUSE", - and: ["event_name": "AdImpression", - "rid": "rule id 1"])) - abuseTelemetry.send(json(for: "VPAID_UNIQUE_EVENT_ABUSE", - and: ["event_name": "AdImpression", - "rid": "rule id 2"])) + abuseTelemetry.send(telemetryJSON(for: "VPAID_UNIQUE_EVENT_ABUSE", + and: ["event_name": "AdImpression" |> json, + "rid": "rule id 1" |> json])) + abuseTelemetry.send(telemetryJSON(for: "VPAID_UNIQUE_EVENT_ABUSE", + and: ["event_name": "AdImpression" |> json, + "rid": "rule id 2" |> json])) } } @@ -121,11 +116,11 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - jsTelemetry.send(json(for: "VPAID_JAVASCRIPT_EVALUATION_ERROR", - and: ["rid": "rule", - "error_code": "\(error.errorCode)", - "error_description": (error as NSError).description, - "error_additional_info": error.errorUserInfo])) + jsTelemetry.send(telemetryJSON(for: "VPAID_JAVASCRIPT_EVALUATION_ERROR", + and: ["rid": "rule" |> json, + "error_code": "\(error.errorCode)" |> json, + "error_description": (error as NSError).description |> json, + "error_additional_info": error.errorUserInfo.description |> json])) } } @@ -140,23 +135,23 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - jsTelemetry.send(json(for: "VPAID_JAVASCRIPT_EVALUATION_ERROR", - and: ["rid": "rule1", - "error_code": "\(error1.errorCode)", - "error_description": (error1 as NSError).description, - "error_additional_info": error1.errorUserInfo])) + jsTelemetry.send(telemetryJSON(for: "VPAID_JAVASCRIPT_EVALUATION_ERROR", + and: ["rid": "rule1" |> json, + "error_code": "\(error1.errorCode)" |> json, + "error_description": (error1 as NSError).description |> json, + "error_additional_info": error1.errorUserInfo.description |> json])) - jsTelemetry.send(json(for: "VPAID_JAVASCRIPT_EVALUATION_ERROR", - and: ["rid": "rule2", - "error_code": "\(error2.errorCode)", - "error_description": (error2 as NSError).description, - "error_additional_info": error2.errorUserInfo])) + jsTelemetry.send(telemetryJSON(for: "VPAID_JAVASCRIPT_EVALUATION_ERROR", + and: ["rid": "rule2" |> json, + "error_code": "\(error2.errorCode)" |> json, + "error_description": (error2 as NSError).description |> json, + "error_additional_info": error2.errorUserInfo.description |> json])) - jsTelemetry.send(json(for: "VPAID_JAVASCRIPT_EVALUATION_ERROR", - and: ["rid": "rule2", - "error_code": "\(error3.errorCode)", - "error_description": (error3 as NSError).description, - "error_additional_info": error3.errorUserInfo])) + jsTelemetry.send(telemetryJSON(for: "VPAID_JAVASCRIPT_EVALUATION_ERROR", + and: ["rid": "rule2" |> json, + "error_code": "\(error3.errorCode)" |> json, + "error_description": (error3 as NSError).description |> json, + "error_additional_info": error3.errorUserInfo.description |> json])) } } @@ -167,8 +162,8 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - unsupportedVPAID.send(json(for: "VPAID_UNSUPPORTED_VERSION_ERROR", - and: ["rid": "rule id"])) + unsupportedVPAID.send(telemetryJSON(for: "VPAID_UNSUPPORTED_VERSION_ERROR", + and: ["rid": "rule id" |> json])) } } @@ -180,11 +175,11 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - unsupportedVPAID.send(json(for: "VPAID_UNSUPPORTED_VERSION_ERROR", - and: ["rid": "rule 1"])) + unsupportedVPAID.send(telemetryJSON(for: "VPAID_UNSUPPORTED_VERSION_ERROR", + and: ["rid": "rule 1" |> json])) - unsupportedVPAID.send(json(for: "VPAID_UNSUPPORTED_VERSION_ERROR", - and: ["rid": "rule 2"])) + unsupportedVPAID.send(telemetryJSON(for: "VPAID_UNSUPPORTED_VERSION_ERROR", + and: ["rid": "rule 2" |> json])) } } @@ -195,8 +190,8 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - adStartTimeout.send(json(for: "START_TIMEOUT_REACHED", - and: ["rid": "rule 1"])) + adStartTimeout.send(telemetryJSON(for: "START_TIMEOUT_REACHED", + and: ["rid": "rule 1" |> json])) } } func testAdStartTimeoutOnSecondSession() { @@ -207,8 +202,8 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - adStartTimeout.send(json(for: "START_TIMEOUT_REACHED", - and: ["rid": "rule 2"])) + adStartTimeout.send(telemetryJSON(for: "START_TIMEOUT_REACHED", + and: ["rid": "rule 2" |> json])) } } func testAdStartTimeoutTwoSessions() { @@ -219,10 +214,10 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - adStartTimeout.send(json(for: "START_TIMEOUT_REACHED", - and: ["rid": "rule 1"])) - adStartTimeout.send(json(for: "START_TIMEOUT_REACHED", - and: ["rid": "rule 2"])) + adStartTimeout.send(telemetryJSON(for: "START_TIMEOUT_REACHED", + and: ["rid": "rule 1" |> json])) + adStartTimeout.send(telemetryJSON(for: "START_TIMEOUT_REACHED", + and: ["rid": "rule 2" |> json])) } } func testOpenMeasurementInitiated() { @@ -234,10 +229,10 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - successInitializationReporter.send(json(for: "OM_SDK_INITIATED", - and: ["rid": "rule 1"])) - successInitializationReporter.send(json(for: "OM_SDK_INITIATED", - and: ["rid": "rule 2"])) + successInitializationReporter.send(telemetryJSON(for: "OM_SDK_INITIATED", + and: ["rid": "rule 1" |> json])) + successInitializationReporter.send(telemetryJSON(for: "OM_SDK_INITIATED", + and: ["rid": "rule 2" |> json])) } } func testOpenMeasurementFailed() { @@ -252,12 +247,12 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - failedConfigurationReporter.send(json(for: "OM_SDK_ERROR", - and: ["message": error.localizedDescription, - "rid": "rule 1"])) - failedConfigurationReporter.send(json(for: "OM_SDK_ERROR", - and: ["message": error.localizedDescription, - "rid": "rule 2"])) + failedConfigurationReporter.send(telemetryJSON(for: "OM_SDK_ERROR", + and: ["message": error.localizedDescription |> json, + "rid": "rule 1" |> json])) + failedConfigurationReporter.send(telemetryJSON(for: "OM_SDK_ERROR", + and: ["message": error.localizedDescription |> json, + "rid": "rule 2" |> json])) } } func testServiceScriptFetchingFailed() { @@ -271,8 +266,8 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - scriptFetchingFailedReporter.send(json(for: "OM_SDK_SCRIPT_FETCHING_FAILED", - and: ["message": error.localizedDescription])) + scriptFetchingFailedReporter.send(telemetryJSON(for: "OM_SDK_SCRIPT_FETCHING_FAILED", + and: ["message": error.localizedDescription |> json])) } } @@ -288,8 +283,8 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - vrmProcessing.vrm.send(json(for: "VRM_PROCESSING_TIME", - and: ["time": 2500])) + vrmProcessing.vrm.send(telemetryJSON(for: "VRM_PROCESSING_TIME", + and: ["time": 2500 |> json])) } } @@ -305,8 +300,8 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - mp4AdBuffering.mp4Ad.send(json(for: "AD_BUFFERING_TIME", - and: ["time": 2500])) + mp4AdBuffering.mp4Ad.send(telemetryJSON(for: "AD_BUFFERING_TIME", + and: ["time": 2500 |> json])) } } @@ -324,19 +319,19 @@ class TelemetryMetricsTest: XCTestCase { } recorder.verify { - contentBuffering.content.send(json(for: "VIDEO_BUFFERING_TIME", - and: ["time": 2500])) + contentBuffering.content.send(telemetryJSON(for: "VIDEO_BUFFERING_TIME", + and: ["time": 2500 |> json])) } } - private func json(for name: String, and value: JSON) -> JSON { - return [ - "context" : [:], + private func telemetryJSON(for type: String, and value: [String: JSøN]) -> Telemetry.TelemetryJSON { + let data: [String: JSøN] = [ "data" : [ - "type" : name, - "value": value - ] + "type" : type |> json, + "value": value |> json + ] |> json ] + return .init(context: [:], data: data |> json) } private func eventErrorWithName(name: String) -> VPAIDErrors.UniqueEventError { diff --git a/sources/video detectors/Player_VideoEvents.swift b/sources/video detectors/Player_VideoEvents.swift index 1d27a31..4c6ce0f 100644 --- a/sources/video detectors/Player_VideoEvents.swift +++ b/sources/video detectors/Player_VideoEvents.swift @@ -60,7 +60,7 @@ extension Player { /// Add video events /// - parameter playbackEvents: Playback events struct with callbacks. /// - returns: Dispose lambda to be called when events no longer needed. - @available(*, deprecated: 2.12) + @available(*, deprecated) public func addVideoEvents(playbackEvents: PlaybackEvents) -> Player.PropsObserverDispose { return addVideoEvents(on: .main, playbackEvents: playbackEvents) }