@npaw/forge-react-native 9.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/LICENSE +10 -0
  2. package/NPAWForgeReactNative.podspec +44 -0
  3. package/README.md +51 -0
  4. package/android/build.gradle +77 -0
  5. package/android/src/main/AndroidManifest.xml +14 -0
  6. package/android/src/main/java/com/npaw/forge/reactnative/ForgeDtoMapper.java +295 -0
  7. package/android/src/main/java/com/npaw/forge/reactnative/ForgeInstanceRegistry.java +61 -0
  8. package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgeModule.java +735 -0
  9. package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgePackage.java +53 -0
  10. package/android/src/test/java/com/npaw/forge/reactnative/ForgeDtoMapperTest.java +133 -0
  11. package/ios/ForgeApple/ForgeKernelFFI.xcframework/Info.plist +82 -0
  12. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/ForgeKernelFFI.h +17 -0
  13. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/forge_kernelFFI.h +873 -0
  14. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/module.modulemap +7 -0
  15. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/libForgeKernelFFI.a +0 -0
  16. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/ForgeKernelFFI.h +17 -0
  17. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/forge_kernelFFI.h +873 -0
  18. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/module.modulemap +7 -0
  19. package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/libForgeKernelFFI.a +0 -0
  20. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/ForgeKernelFFI.h +17 -0
  21. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/forge_kernelFFI.h +873 -0
  22. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/module.modulemap +7 -0
  23. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/libForgeKernelFFI.a +0 -0
  24. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/ForgeKernelFFI.h +17 -0
  25. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/forge_kernelFFI.h +873 -0
  26. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/module.modulemap +7 -0
  27. package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/libForgeKernelFFI.a +0 -0
  28. package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernel.swift +3931 -0
  29. package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.h +873 -0
  30. package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.modulemap +7 -0
  31. package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAccessLogFacts.swift +152 -0
  32. package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAdapter.swift +877 -0
  33. package/ios/ForgeApple/Sources/NPAWForgeCapture/AttributionCapture.swift +158 -0
  34. package/ios/ForgeApple/Sources/NPAWForgeCapture/CapturePlanGate.swift +189 -0
  35. package/ios/ForgeApple/Sources/NPAWForgeCapture/CrashCapture.swift +120 -0
  36. package/ios/ForgeApple/Sources/NPAWForgeCapture/ForgeAutomaticCapture.swift +639 -0
  37. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsCapture.swift +1176 -0
  38. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsEngine.swift +674 -0
  39. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsMarkers.swift +105 -0
  40. package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsSwiftUI.swift +284 -0
  41. package/ios/ForgeApple/Sources/NPAWForgeCapture/SwiftUICapture.swift +44 -0
  42. package/ios/ForgeApple/Sources/NPAWForgeCore/AdAdapter.swift +199 -0
  43. package/ios/ForgeApple/Sources/NPAWForgeCore/AdSemantics.swift +136 -0
  44. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeApple.swift +596 -0
  45. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeCapturePlan.swift +297 -0
  46. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeClock.swift +29 -0
  47. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeLogging.swift +109 -0
  48. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeOutcome.swift +89 -0
  49. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeRuntime.swift +291 -0
  50. package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeStorage.swift +70 -0
  51. package/ios/ForgeApple/Sources/NPAWForgeCore/ManualAdReporter.swift +180 -0
  52. package/ios/ForgeApple/Sources/NPAWForgeCore/NPAWForge.swift +453 -0
  53. package/ios/ForgeApple/Sources/NPAWForgeCore/NativeUniFFIRuntime.swift +594 -0
  54. package/ios/ForgeApple/Sources/NPAWForgeCore/NetworkRequestRecord.swift +191 -0
  55. package/ios/ForgeApple/Sources/NPAWForgeCore/ObjectiveCWrappers.swift +191 -0
  56. package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerAdapter.swift +299 -0
  57. package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerSemantics.swift +266 -0
  58. package/ios/ForgeApple/Sources/NPAWForgeCore/PrivacySanitizer.swift +59 -0
  59. package/ios/ForgeApple/Sources/NPAWForgeCore/VideoAdapter.swift +478 -0
  60. package/ios/ForgeApple/Sources/NPAWForgeCore/VideoOptions.swift +431 -0
  61. package/ios/ForgeApple/Sources/NPAWForgeNetworkCapture/ForgeNetworkCapture.swift +116 -0
  62. package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/NPAWForgeNetworkSwizzler.m +293 -0
  63. package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/include/NPAWForgeNetworkSwizzler.h +33 -0
  64. package/ios/ForgeModuleImpl.swift +1131 -0
  65. package/ios/NPAWForgeTurboModule.mm +399 -0
  66. package/package.json +33 -0
  67. package/src/ForgeAdReporter.ts +101 -0
  68. package/src/ForgeVideo.ts +169 -0
  69. package/src/NPAWForge.ts +177 -0
  70. package/src/capture.ts +192 -0
  71. package/src/events.ts +40 -0
  72. package/src/index.ts +37 -0
  73. package/src/specs/NativeNPAWForge.ts +168 -0
  74. package/src/types.ts +114 -0
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Copyright (C) NPAW - All Rights Reserved
3
+ *
4
+ * This source code is protected under international copyright law. All rights
5
+ * reserved and protected by the copyright holders.
6
+ * This file is confidential and only available to authorized individuals with the
7
+ * permission of the copyright holders. If you encounter this file and do not have
8
+ * permission, please contact the copyright holders and delete this file.
9
+ *
10
+ *
11
+ * @author: Victor Soto <victor.soto@nicepeopleatwork.com>
12
+ * @description: Apple runtime abstraction that the Swift facade uses to reach the Rust core.
13
+ */
14
+
15
+ import Foundation
16
+
17
+ public struct RuntimeVideoView: Equatable {
18
+ public let id: Int64
19
+
20
+ public init(id: Int64) {
21
+ self.id = id
22
+ }
23
+ }
24
+
25
+ public struct ForgeRecordedEvent: Equatable {
26
+ public let eventName: String
27
+ public let timestampMs: Int64
28
+ public let payload: [String: String]
29
+ public let urgent: Bool
30
+ public let viewKey: String?
31
+
32
+ public init(
33
+ eventName: String,
34
+ timestampMs: Int64,
35
+ payload: [String: String],
36
+ urgent: Bool = false,
37
+ viewKey: String? = nil
38
+ ) {
39
+ self.eventName = eventName
40
+ self.timestampMs = timestampMs
41
+ self.payload = payload
42
+ self.urgent = urgent
43
+ self.viewKey = viewKey
44
+ }
45
+ }
46
+
47
+ public protocol RuntimeContentMetadataProvider: AnyObject {
48
+ func metadataSnapshot(timemarkMs: Int64, elapsedRealtimeMs: Int64) -> MetadataSnapshot?
49
+ }
50
+
51
+ public protocol RuntimeVideoPreSendHook: AnyObject {
52
+ func willSendRequest(
53
+ eventName: String,
54
+ view: RuntimeVideoView,
55
+ serviceName: String,
56
+ params: inout [String: String]
57
+ )
58
+ }
59
+
60
+ public protocol RuntimeSessionPreSendHook: AnyObject {
61
+ func willSendRequest(serviceName: String, params: inout [String: String])
62
+ }
63
+
64
+ public protocol ForgeRuntime: AnyObject {
65
+ func start() -> ForgeOutcome
66
+ func setLogLevel(_ level: ForgeLogLevel) -> ForgeOutcome
67
+ func createVideoView(timestampMs: Int64, options: VideoOptions) -> RuntimeVideoView
68
+ func recordEvent(_ event: ForgeRecordedEvent) -> ForgeOutcome
69
+ /// Records a typed identity transition (`loginSuccess`, `loginFailed`,
70
+ /// `logout`, `profileCreated`, `profileSelected`, `profileDeleted`). The
71
+ /// kernel consumes the recognized fields (`userId`, `profileId`,
72
+ /// `profileType`, `method`, `errorCode`, `reason`, `userType`), forwards
73
+ /// the rest as extra session-event params, and emits the taxonomy event.
74
+ func recordIdentityEvent(kind: String, timemarkMs: Int64, fields: [String: String]) -> ForgeOutcome
75
+ func recordApplicationForeground(_ foreground: Bool, timestampMs: Int64) -> ForgeOutcome
76
+ func recordNetworkEvent(_ record: NetworkRequestRecord) -> ForgeOutcome
77
+ func recordPlayerEvent(
78
+ view: RuntimeVideoView,
79
+ event: PlayerEvent,
80
+ timemarkMs: Int64,
81
+ elapsedRealtimeMs: Int64,
82
+ options: VideoOptions,
83
+ metadata: PlayerMetadataSnapshot
84
+ ) -> ForgeOutcome
85
+ func recordAdEvent(
86
+ view: RuntimeVideoView,
87
+ event: AdEvent,
88
+ timemarkMs: Int64,
89
+ elapsedRealtimeMs: Int64,
90
+ options: VideoOptions,
91
+ metadata: AdMetadataSnapshot,
92
+ adapterVersion: String?
93
+ ) -> ForgeOutcome
94
+ func registerContentMetadataProvider(view: RuntimeVideoView, provider: RuntimeContentMetadataProvider?) -> ForgeOutcome
95
+ func registerVideoPreSendHook(view: RuntimeVideoView, hook: RuntimeVideoPreSendHook?) -> ForgeOutcome
96
+ func registerSessionPreSendHook(_ hook: RuntimeSessionPreSendHook?) -> ForgeOutcome
97
+ func destroyVideoView(_ view: RuntimeVideoView) -> ForgeOutcome
98
+ func snapshotEvents() -> [ForgeRecordedEvent]
99
+ func destroy() -> ForgeOutcome
100
+ /// Registers (or clears with `nil`) the kernel capture-plan observer. The
101
+ /// latest plan is replayed to a late-registered listener when available.
102
+ func setCapturePlanListener(_ listener: ((ForgeCapturePlan) -> Void)?) -> ForgeOutcome
103
+ /// Latest kernel-emitted capture plan, or `nil` before the first emission.
104
+ func capturePlan() -> ForgeCapturePlan?
105
+ }
106
+
107
+ /// Additive defaults so existing `ForgeRuntime` conformances (including the
108
+ /// non-native fallback) keep compiling; runtimes without a kernel capture
109
+ /// plan simply never deliver one and bindings stay on local options only.
110
+ public extension ForgeRuntime {
111
+ func setCapturePlanListener(_ listener: ((ForgeCapturePlan) -> Void)?) -> ForgeOutcome { .accepted }
112
+ func capturePlan() -> ForgeCapturePlan? { nil }
113
+ /// Additive default: runtimes without kernel identity support report the
114
+ /// capability gap instead of silently accepting the transition.
115
+ func recordIdentityEvent(kind: String, timemarkMs: Int64, fields: [String: String]) -> ForgeOutcome { .unsupportedCapability }
116
+ }
117
+
118
+ public protocol ForgeRuntimeFactory: AnyObject {
119
+ func create(config: ForgeAppleConfig, services: ForgeAppleServices) -> ForgeRuntime
120
+ }
121
+
122
+ public final class UnavailableForgeRuntime: ForgeRuntime {
123
+ private let reason: String
124
+
125
+ public init(reason: String) {
126
+ self.reason = reason
127
+ }
128
+
129
+ public func start() -> ForgeOutcome { failed() }
130
+ public func setLogLevel(_ level: ForgeLogLevel) -> ForgeOutcome { .accepted }
131
+ public func createVideoView(timestampMs: Int64, options: VideoOptions) -> RuntimeVideoView { RuntimeVideoView(id: -1) }
132
+ public func recordEvent(_ event: ForgeRecordedEvent) -> ForgeOutcome { failed() }
133
+ public func recordIdentityEvent(kind: String, timemarkMs: Int64, fields: [String: String]) -> ForgeOutcome { failed() }
134
+ public func recordApplicationForeground(_ foreground: Bool, timestampMs: Int64) -> ForgeOutcome { failed() }
135
+ public func recordNetworkEvent(_ record: NetworkRequestRecord) -> ForgeOutcome { failed() }
136
+ public func recordPlayerEvent(
137
+ view: RuntimeVideoView,
138
+ event: PlayerEvent,
139
+ timemarkMs: Int64,
140
+ elapsedRealtimeMs: Int64,
141
+ options: VideoOptions,
142
+ metadata: PlayerMetadataSnapshot
143
+ ) -> ForgeOutcome { failed() }
144
+ public func recordAdEvent(
145
+ view: RuntimeVideoView,
146
+ event: AdEvent,
147
+ timemarkMs: Int64,
148
+ elapsedRealtimeMs: Int64,
149
+ options: VideoOptions,
150
+ metadata: AdMetadataSnapshot,
151
+ adapterVersion: String?
152
+ ) -> ForgeOutcome { failed() }
153
+ public func registerContentMetadataProvider(view: RuntimeVideoView, provider: RuntimeContentMetadataProvider?) -> ForgeOutcome { .accepted }
154
+ public func registerVideoPreSendHook(view: RuntimeVideoView, hook: RuntimeVideoPreSendHook?) -> ForgeOutcome { .accepted }
155
+ public func registerSessionPreSendHook(_ hook: RuntimeSessionPreSendHook?) -> ForgeOutcome { .accepted }
156
+ public func destroyVideoView(_ view: RuntimeVideoView) -> ForgeOutcome { .accepted }
157
+ public func snapshotEvents() -> [ForgeRecordedEvent] { [] }
158
+ public func destroy() -> ForgeOutcome { .accepted }
159
+
160
+ private func failed() -> ForgeOutcome {
161
+ .failedWithDiagnostic(reason)
162
+ }
163
+ }
164
+
165
+ public final class NativeForgeRuntimeFactory: ForgeRuntimeFactory {
166
+ public init() {}
167
+
168
+ public func create(config: ForgeAppleConfig, services: ForgeAppleServices) -> ForgeRuntime {
169
+ #if FORGE_HAS_UNIFFI
170
+ return UniFFIForgeRuntime(config: config)
171
+ #else
172
+ UnavailableForgeRuntime(reason: "native_runtime_unavailable")
173
+ #endif
174
+ }
175
+ }
176
+
177
+ public final class RecordingForgeRuntime: ForgeRuntime {
178
+ public private(set) var events: [ForgeRecordedEvent] = []
179
+ public private(set) var identityEvents: [(kind: String, timemarkMs: Int64, fields: [String: String])] = []
180
+ public private(set) var playerEvents: [(RuntimeVideoView, PlayerEvent, PlayerMetadataSnapshot)] = []
181
+ public private(set) var playerEventTimestamps: [(timemarkMs: Int64, elapsedRealtimeMs: Int64)] = []
182
+ public private(set) var playerEventOptions: [VideoOptions] = []
183
+ public private(set) var adEvents: [(RuntimeVideoView, AdEvent, AdMetadataSnapshot)] = []
184
+ public private(set) var adEventTimestamps: [(timemarkMs: Int64, elapsedRealtimeMs: Int64)] = []
185
+ public private(set) var networkEvents: [NetworkRequestRecord] = []
186
+ public private(set) var createdVideoTimestamps: [Int64] = []
187
+ public private(set) var contentMetadataProviders: [Int64: RuntimeContentMetadataProvider] = [:]
188
+ public private(set) var videoPreSendHooks: [Int64: RuntimeVideoPreSendHook] = [:]
189
+ public private(set) var destroyedViewIds: [Int64] = []
190
+ public private(set) var capturePlanListener: ((ForgeCapturePlan) -> Void)?
191
+ public private(set) var latestCapturePlan: ForgeCapturePlan?
192
+ private var nextViewId: Int64 = 1
193
+
194
+ public init() {}
195
+
196
+ public func start() -> ForgeOutcome { .accepted }
197
+ public func setLogLevel(_ level: ForgeLogLevel) -> ForgeOutcome { .accepted }
198
+ public func createVideoView(timestampMs: Int64, options: VideoOptions) -> RuntimeVideoView {
199
+ createdVideoTimestamps.append(timestampMs)
200
+ defer { nextViewId += 1 }
201
+ return RuntimeVideoView(id: nextViewId)
202
+ }
203
+ public func recordEvent(_ event: ForgeRecordedEvent) -> ForgeOutcome {
204
+ events.append(event)
205
+ return .accepted
206
+ }
207
+ public func recordIdentityEvent(kind: String, timemarkMs: Int64, fields: [String: String]) -> ForgeOutcome {
208
+ identityEvents.append((kind, timemarkMs, fields))
209
+ return .accepted
210
+ }
211
+ public func recordApplicationForeground(_ foreground: Bool, timestampMs: Int64) -> ForgeOutcome {
212
+ events.append(
213
+ ForgeRecordedEvent(
214
+ eventName: foreground ? "raw_application_foreground" : "raw_application_background",
215
+ timestampMs: timestampMs,
216
+ payload: [:]
217
+ )
218
+ )
219
+ return .accepted
220
+ }
221
+ public func recordNetworkEvent(_ record: NetworkRequestRecord) -> ForgeOutcome {
222
+ networkEvents.append(record)
223
+ return .accepted
224
+ }
225
+ public func recordPlayerEvent(
226
+ view: RuntimeVideoView,
227
+ event: PlayerEvent,
228
+ timemarkMs: Int64,
229
+ elapsedRealtimeMs: Int64,
230
+ options: VideoOptions,
231
+ metadata: PlayerMetadataSnapshot
232
+ ) -> ForgeOutcome {
233
+ playerEvents.append((view, event, metadata))
234
+ playerEventTimestamps.append((timemarkMs, elapsedRealtimeMs))
235
+ playerEventOptions.append(options)
236
+ return .accepted
237
+ }
238
+ public func recordAdEvent(
239
+ view: RuntimeVideoView,
240
+ event: AdEvent,
241
+ timemarkMs: Int64,
242
+ elapsedRealtimeMs: Int64,
243
+ options: VideoOptions,
244
+ metadata: AdMetadataSnapshot,
245
+ adapterVersion: String?
246
+ ) -> ForgeOutcome {
247
+ adEvents.append((view, event, metadata))
248
+ adEventTimestamps.append((timemarkMs, elapsedRealtimeMs))
249
+ return .accepted
250
+ }
251
+ public func registerContentMetadataProvider(view: RuntimeVideoView, provider: RuntimeContentMetadataProvider?) -> ForgeOutcome {
252
+ if let provider {
253
+ contentMetadataProviders[view.id] = provider
254
+ } else {
255
+ contentMetadataProviders.removeValue(forKey: view.id)
256
+ }
257
+ return .accepted
258
+ }
259
+ public func registerVideoPreSendHook(view: RuntimeVideoView, hook: RuntimeVideoPreSendHook?) -> ForgeOutcome {
260
+ if let hook {
261
+ videoPreSendHooks[view.id] = hook
262
+ } else {
263
+ videoPreSendHooks.removeValue(forKey: view.id)
264
+ }
265
+ return .accepted
266
+ }
267
+ public func registerSessionPreSendHook(_ hook: RuntimeSessionPreSendHook?) -> ForgeOutcome { .accepted }
268
+ public func destroyVideoView(_ view: RuntimeVideoView) -> ForgeOutcome {
269
+ destroyedViewIds.append(view.id)
270
+ contentMetadataProviders.removeValue(forKey: view.id)
271
+ videoPreSendHooks.removeValue(forKey: view.id)
272
+ return .accepted
273
+ }
274
+ public func snapshotEvents() -> [ForgeRecordedEvent] { events }
275
+ public func destroy() -> ForgeOutcome { .accepted }
276
+ public func setCapturePlanListener(_ listener: ((ForgeCapturePlan) -> Void)?) -> ForgeOutcome {
277
+ capturePlanListener = listener
278
+ if let listener, let latest = latestCapturePlan {
279
+ listener(latest)
280
+ }
281
+ return .accepted
282
+ }
283
+ public func capturePlan() -> ForgeCapturePlan? { latestCapturePlan }
284
+
285
+ /// Test hook mirroring the native runtime's push path: stores the latest
286
+ /// plan and dispatches it to the registered listener.
287
+ public func emitCapturePlan(_ plan: ForgeCapturePlan) {
288
+ latestCapturePlan = plan
289
+ capturePlanListener?(plan)
290
+ }
291
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Copyright (C) NPAW - All Rights Reserved
3
+ *
4
+ * This source code is protected under international copyright law. All rights
5
+ * reserved and protected by the copyright holders.
6
+ * This file is confidential and only available to authorized individuals with the
7
+ * permission of the copyright holders. If you encounter this file and do not have
8
+ * permission, please contact the copyright holders and delete this file.
9
+ *
10
+ *
11
+ * @author: Victor Soto <victor.soto@nicepeopleatwork.com>
12
+ * @description: Apple storage abstraction used by the native runtime for bounded remote-config persistence.
13
+ */
14
+
15
+ import Foundation
16
+
17
+ public protocol ForgeStorage: AnyObject {
18
+ func read(_ key: String) -> String?
19
+ func write(_ key: String, value: String) -> ForgeOutcome
20
+ func delete(_ key: String) -> ForgeOutcome
21
+ }
22
+
23
+ public final class MemoryStorage: ForgeStorage {
24
+ private var values: [String: String] = [:]
25
+ private let lock = NSLock()
26
+
27
+ public init() {}
28
+
29
+ public func read(_ key: String) -> String? {
30
+ lock.lock()
31
+ defer { lock.unlock() }
32
+ return values[key]
33
+ }
34
+
35
+ public func write(_ key: String, value: String) -> ForgeOutcome {
36
+ lock.lock()
37
+ defer { lock.unlock() }
38
+ values[key] = value
39
+ return .accepted
40
+ }
41
+
42
+ public func delete(_ key: String) -> ForgeOutcome {
43
+ lock.lock()
44
+ defer { lock.unlock() }
45
+ values.removeValue(forKey: key)
46
+ return .accepted
47
+ }
48
+ }
49
+
50
+ public final class UserDefaultsStorage: ForgeStorage {
51
+ private let defaults: UserDefaults
52
+
53
+ public init(suiteName: String = "com.npaw.forge.apple.storage") {
54
+ self.defaults = UserDefaults(suiteName: suiteName) ?? .standard
55
+ }
56
+
57
+ public func read(_ key: String) -> String? {
58
+ defaults.string(forKey: key)
59
+ }
60
+
61
+ public func write(_ key: String, value: String) -> ForgeOutcome {
62
+ defaults.set(value, forKey: key)
63
+ return defaults.synchronize() ? .accepted : .failedWithDiagnostic("storage_write_failed")
64
+ }
65
+
66
+ public func delete(_ key: String) -> ForgeOutcome {
67
+ defaults.removeObject(forKey: key)
68
+ return defaults.synchronize() ? .accepted : .failedWithDiagnostic("storage_delete_failed")
69
+ }
70
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Copyright (C) NPAW - All Rights Reserved
3
+ *
4
+ * This source code is protected under international copyright law. All rights
5
+ * reserved and protected by the copyright holders.
6
+ * This file is confidential and only available to authorized individuals with the
7
+ * permission of the copyright holders. If you encounter this file and do not have
8
+ * permission, please contact the copyright holders and delete this file.
9
+ *
10
+ *
11
+ * @author: Victor Soto <victor.soto@nicepeopleatwork.com>
12
+ * @description: Manual Apple ad reporting facade for adapterless video integrations.
13
+ */
14
+
15
+ import Foundation
16
+
17
+ public final class ManualAdReporter {
18
+ private weak var videoAdapter: VideoAdapter?
19
+ private let adapterVersion: String?
20
+
21
+ init(videoAdapter: VideoAdapter, adapterVersion: String?) {
22
+ self.videoAdapter = videoAdapter
23
+ self.adapterVersion = adapterVersion
24
+ }
25
+
26
+ public func fireManifest(
27
+ metadata: AdMetadataSnapshot? = nil,
28
+ params: [String: String]? = [:],
29
+ timestampMs: Int64 = 0
30
+ ) -> ForgeOutcome {
31
+ record(.manifest, metadata ?? adMetadataFromParams(params), timestampMs)
32
+ }
33
+
34
+ public func fireBreakStart(
35
+ metadata: AdMetadataSnapshot? = nil,
36
+ params: [String: String]? = [:],
37
+ timestampMs: Int64 = 0
38
+ ) -> ForgeOutcome {
39
+ record(.breakStart, metadata ?? adMetadataFromParams(params), timestampMs)
40
+ }
41
+
42
+ public func fireAdInit(
43
+ metadata: AdMetadataSnapshot? = nil,
44
+ params: [String: String]? = [:],
45
+ timestampMs: Int64 = 0
46
+ ) -> ForgeOutcome {
47
+ record(.adInit, metadata ?? adMetadataFromParams(params), timestampMs)
48
+ }
49
+
50
+ public func fireStart(
51
+ metadata: AdMetadataSnapshot? = nil,
52
+ params: [String: String]? = [:],
53
+ timestampMs: Int64 = 0
54
+ ) -> ForgeOutcome {
55
+ record(.start, metadata ?? adMetadataFromParams(params), timestampMs)
56
+ }
57
+
58
+ public func fireJoin(
59
+ metadata: AdMetadataSnapshot? = nil,
60
+ params: [String: String]? = [:],
61
+ timestampMs: Int64 = 0
62
+ ) -> ForgeOutcome {
63
+ record(.join, metadata ?? adMetadataFromParams(params), timestampMs)
64
+ }
65
+
66
+ public func firePause(
67
+ metadata: AdMetadataSnapshot? = nil,
68
+ params: [String: String]? = [:],
69
+ timestampMs: Int64 = 0
70
+ ) -> ForgeOutcome {
71
+ record(.pause, metadata ?? adMetadataFromParams(params), timestampMs)
72
+ }
73
+
74
+ public func fireResume(
75
+ metadata: AdMetadataSnapshot? = nil,
76
+ params: [String: String]? = [:],
77
+ timestampMs: Int64 = 0
78
+ ) -> ForgeOutcome {
79
+ record(.resume, metadata ?? adMetadataFromParams(params), timestampMs)
80
+ }
81
+
82
+ public func fireBufferBegin(
83
+ metadata: AdMetadataSnapshot? = nil,
84
+ params: [String: String]? = [:],
85
+ timestampMs: Int64 = 0
86
+ ) -> ForgeOutcome {
87
+ record(.bufferBegin, metadata ?? adMetadataFromParams(params), timestampMs)
88
+ }
89
+
90
+ public func fireBufferEnd(
91
+ metadata: AdMetadataSnapshot? = nil,
92
+ params: [String: String]? = [:],
93
+ timestampMs: Int64 = 0
94
+ ) -> ForgeOutcome {
95
+ record(.bufferEnd, metadata ?? adMetadataFromParams(params), timestampMs)
96
+ }
97
+
98
+ public func fireQuartile(_ quartile: Int? = nil, timestampMs: Int64 = 0) -> ForgeOutcome {
99
+ var metadata = AdMetadataSnapshot()
100
+ metadata.quartile = quartile
101
+ return record(.quartile, metadata, timestampMs)
102
+ }
103
+
104
+ public func fireClick(
105
+ metadata: AdMetadataSnapshot? = nil,
106
+ params: [String: String]? = [:],
107
+ timestampMs: Int64 = 0
108
+ ) -> ForgeOutcome {
109
+ record(.click, metadata ?? adMetadataFromParams(params), timestampMs)
110
+ }
111
+
112
+ public func fireClick(adUrl: String?) -> ForgeOutcome {
113
+ fireClick(params: adUrl.map { ["adUrl": $0] } ?? [:])
114
+ }
115
+
116
+ public func fireSkip(timestampMs: Int64 = 0) -> ForgeOutcome {
117
+ record(.skip, AdMetadataSnapshot(), timestampMs)
118
+ }
119
+
120
+ public func fireStop(
121
+ metadata: AdMetadataSnapshot? = nil,
122
+ params: [String: String]? = [:],
123
+ timestampMs: Int64 = 0
124
+ ) -> ForgeOutcome {
125
+ record(.stop, metadata ?? adMetadataFromParams(params), timestampMs)
126
+ }
127
+
128
+ public func fireError(
129
+ errorCode: String? = nil,
130
+ errorMessage: String? = nil,
131
+ errorMetadata: String? = nil,
132
+ timestampMs: Int64 = 0
133
+ ) -> ForgeOutcome {
134
+ var metadata = AdMetadataSnapshot()
135
+ metadata.errorCode = errorCode
136
+ metadata.errorMessage = errorMessage
137
+ metadata.errorMetadata = errorMetadata
138
+ return record(.error, metadata, timestampMs)
139
+ }
140
+
141
+ public func fireFatalError(
142
+ errorCode: String? = nil,
143
+ errorMessage: String? = nil,
144
+ errorMetadata: String? = nil,
145
+ timestampMs: Int64 = 0
146
+ ) -> ForgeOutcome {
147
+ var metadata = AdMetadataSnapshot()
148
+ metadata.errorCode = errorCode
149
+ metadata.errorMessage = errorMessage
150
+ metadata.errorMetadata = errorMetadata
151
+ metadata.errorLevel = "fatal"
152
+ return record(.fatalError, metadata, timestampMs)
153
+ }
154
+
155
+ public func fireBreakStop(
156
+ metadata: AdMetadataSnapshot? = nil,
157
+ params: [String: String]? = [:],
158
+ timestampMs: Int64 = 0
159
+ ) -> ForgeOutcome {
160
+ record(.breakStop, metadata ?? adMetadataFromParams(params), timestampMs)
161
+ }
162
+
163
+ public func fireDestroy(timestampMs: Int64 = 0) -> ForgeOutcome {
164
+ guard videoAdapter?.isDestroyed == false else { return .ignoredInvalidState }
165
+ return .unsupportedCapability
166
+ }
167
+
168
+ private func record(_ event: AdEvent, _ metadata: AdMetadataSnapshot, _ timestampMs: Int64) -> ForgeOutcome {
169
+ videoAdapter?.recordAdEvent(
170
+ event: event,
171
+ timestampMs: timestampMs,
172
+ metadata: metadata,
173
+ adapterVersion: adapterVersion
174
+ ) ?? .ignoredInvalidState
175
+ }
176
+
177
+ private func adMetadataFromParams(_ params: [String: String]?) -> AdMetadataSnapshot {
178
+ videoAdapter?.adMetadataFromParams(params) ?? AdMetadataSnapshot()
179
+ }
180
+ }