@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.
- package/LICENSE +10 -0
- package/NPAWForgeReactNative.podspec +44 -0
- package/README.md +51 -0
- package/android/build.gradle +77 -0
- package/android/src/main/AndroidManifest.xml +14 -0
- package/android/src/main/java/com/npaw/forge/reactnative/ForgeDtoMapper.java +295 -0
- package/android/src/main/java/com/npaw/forge/reactnative/ForgeInstanceRegistry.java +61 -0
- package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgeModule.java +735 -0
- package/android/src/main/java/com/npaw/forge/reactnative/NPAWForgePackage.java +53 -0
- package/android/src/test/java/com/npaw/forge/reactnative/ForgeDtoMapperTest.java +133 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/Info.plist +82 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/ios-arm64_x86_64-simulator/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/ForgeKernelFFI.h +17 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/Headers/module.modulemap +7 -0
- package/ios/ForgeApple/ForgeKernelFFI.xcframework/tvos-arm64-simulator/libForgeKernelFFI.a +0 -0
- package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernel.swift +3931 -0
- package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.h +873 -0
- package/ios/ForgeApple/Sources/ForgeKernelUniFFI/forge_kernelFFI.modulemap +7 -0
- package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAccessLogFacts.swift +152 -0
- package/ios/ForgeApple/Sources/NPAWForgeAVPlayerAdapter/AVPlayerAdapter.swift +877 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/AttributionCapture.swift +158 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/CapturePlanGate.swift +189 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/CrashCapture.swift +120 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/ForgeAutomaticCapture.swift +639 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsCapture.swift +1176 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsEngine.swift +674 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsMarkers.swift +105 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SectionsSwiftUI.swift +284 -0
- package/ios/ForgeApple/Sources/NPAWForgeCapture/SwiftUICapture.swift +44 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/AdAdapter.swift +199 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/AdSemantics.swift +136 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeApple.swift +596 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeCapturePlan.swift +297 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeClock.swift +29 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeLogging.swift +109 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeOutcome.swift +89 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeRuntime.swift +291 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ForgeStorage.swift +70 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ManualAdReporter.swift +180 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/NPAWForge.swift +453 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/NativeUniFFIRuntime.swift +594 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/NetworkRequestRecord.swift +191 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/ObjectiveCWrappers.swift +191 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerAdapter.swift +299 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/PlayerSemantics.swift +266 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/PrivacySanitizer.swift +59 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/VideoAdapter.swift +478 -0
- package/ios/ForgeApple/Sources/NPAWForgeCore/VideoOptions.swift +431 -0
- package/ios/ForgeApple/Sources/NPAWForgeNetworkCapture/ForgeNetworkCapture.swift +116 -0
- package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/NPAWForgeNetworkSwizzler.m +293 -0
- package/ios/ForgeApple/Sources/NPAWForgeNetworkCaptureObjC/include/NPAWForgeNetworkSwizzler.h +33 -0
- package/ios/ForgeModuleImpl.swift +1131 -0
- package/ios/NPAWForgeTurboModule.mm +399 -0
- package/package.json +33 -0
- package/src/ForgeAdReporter.ts +101 -0
- package/src/ForgeVideo.ts +169 -0
- package/src/NPAWForge.ts +177 -0
- package/src/capture.ts +192 -0
- package/src/events.ts +40 -0
- package/src/index.ts +37 -0
- package/src/specs/NativeNPAWForge.ts +168 -0
- package/src/types.ts +114 -0
|
@@ -0,0 +1,453 @@
|
|
|
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: Public Swift-first Forge Apple facade.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import Foundation
|
|
16
|
+
|
|
17
|
+
// The generated kernel bridge is only present when the Rust runtime is linked;
|
|
18
|
+
// guarded exactly as in NativeUniFFIRuntime.swift.
|
|
19
|
+
#if FORGE_HAS_UNIFFI
|
|
20
|
+
#if canImport(ForgeKernelUniFFI)
|
|
21
|
+
import ForgeKernelUniFFI
|
|
22
|
+
#endif
|
|
23
|
+
#endif
|
|
24
|
+
|
|
25
|
+
@objc(NPAWForge)
|
|
26
|
+
public final class NPAWForge: NSObject {
|
|
27
|
+
public static let defaultVideoAdapterIdentifier = "default"
|
|
28
|
+
/// Marker meaning "this build does not know its own version".
|
|
29
|
+
///
|
|
30
|
+
/// The authoritative Forge version is resolved by
|
|
31
|
+
/// `scripts/version/resolve-version.sh` and compiled into the Rust
|
|
32
|
+
/// runtime, which substitutes it whenever the Swift layer sends this
|
|
33
|
+
/// marker — so the wire never carries the marker when a Forge runtime is
|
|
34
|
+
/// linked. Only builds with no runtime linked see it.
|
|
35
|
+
///
|
|
36
|
+
/// Deliberately NOT release-shaped: it used to be `9.0.0-local`, which
|
|
37
|
+
/// reached the wire as a plausible-looking version and made internal
|
|
38
|
+
/// builds indistinguishable from a real 9.0.0 release.
|
|
39
|
+
///
|
|
40
|
+
/// `scripts/apple/publish-local-shipped.sh` rewrites this literal to the
|
|
41
|
+
/// release version when staging the shipped package, and fails loudly if
|
|
42
|
+
/// it cannot find it. Keep it a plain stored literal for that reason.
|
|
43
|
+
public static let defaultPluginVersion = "9.6.1"
|
|
44
|
+
public static let defaultAppName = "apple"
|
|
45
|
+
public static let defaultBuildVariant = "release"
|
|
46
|
+
public static let defaultLmaEndpoint = "https://lma.npaw.com"
|
|
47
|
+
|
|
48
|
+
/// The Forge plugin version this build actually reports, matching Android's
|
|
49
|
+
/// `NPAWForge.pluginVersion`.
|
|
50
|
+
///
|
|
51
|
+
/// Precedence deliberately mirrors the runtime's own
|
|
52
|
+
/// `effective_plugin_version`, so this property and the wire always agree:
|
|
53
|
+
///
|
|
54
|
+
/// 1. A patched `defaultPluginVersion` wins. That only happens in a staged
|
|
55
|
+
/// shipped package, where `publish-local-shipped.sh` has written the
|
|
56
|
+
/// release version in, and the release version must stay authoritative.
|
|
57
|
+
/// 2. Otherwise the version compiled into the linked Rust runtime.
|
|
58
|
+
/// 3. Otherwise the marker, for builds with no runtime linked.
|
|
59
|
+
public static var pluginVersion: String {
|
|
60
|
+
if defaultPluginVersion != unresolvedPluginVersionMarker {
|
|
61
|
+
return defaultPluginVersion
|
|
62
|
+
}
|
|
63
|
+
return runtimePluginVersion ?? defaultPluginVersion
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Kept separate from `defaultPluginVersion` so the release patch can
|
|
67
|
+
/// rewrite that literal without also rewriting the comparison value.
|
|
68
|
+
private static let unresolvedPluginVersionMarker = "0.0.0-unresolved"
|
|
69
|
+
|
|
70
|
+
/// The version embedded in the linked Rust runtime, when one is linked.
|
|
71
|
+
///
|
|
72
|
+
/// `forgePluginVersion()` is the UniFFI-generated Swift name for the UDL
|
|
73
|
+
/// declaration `string forge_plugin_version();` — the generator camelCases
|
|
74
|
+
/// it.
|
|
75
|
+
private static var runtimePluginVersion: String? {
|
|
76
|
+
#if FORGE_HAS_UNIFFI
|
|
77
|
+
#if canImport(ForgeKernelUniFFI)
|
|
78
|
+
let version = forgePluginVersion()
|
|
79
|
+
return version.isEmpty ? nil : version
|
|
80
|
+
#else
|
|
81
|
+
return nil
|
|
82
|
+
#endif
|
|
83
|
+
#else
|
|
84
|
+
return nil
|
|
85
|
+
#endif
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public let kernel: ForgeApple
|
|
89
|
+
private var destroyed = false
|
|
90
|
+
private let lock = NSRecursiveLock()
|
|
91
|
+
private var videoAdapters: [VideoAdapter] = []
|
|
92
|
+
|
|
93
|
+
public init(kernel: ForgeApple) {
|
|
94
|
+
self.kernel = kernel
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public func videoBuilder() -> VideoBuilder {
|
|
98
|
+
VideoBuilder(forge: self)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public func subscribeEvents(_ observer: @escaping (ForgeRecordedEvent, ForgeOutcome) -> Void) -> ForgeEventSubscription {
|
|
102
|
+
kernel.subscribeEvents(observer)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Observes kernel-derived capture plans (bitmask + focus/limit knobs +
|
|
106
|
+
/// behavior hints). The latest plan is replayed to listeners added after
|
|
107
|
+
/// the first emission. The kernel intake gates remain the semantic
|
|
108
|
+
/// authority; the plan only tells observers what is worth installing.
|
|
109
|
+
public func addCapturePlanListener(_ listener: @escaping (ForgeCapturePlan) -> Void) -> ForgeEventSubscription {
|
|
110
|
+
kernel.addCapturePlanListener(listener)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/// Latest kernel-emitted capture plan, or `nil` before the first
|
|
114
|
+
/// emission (pre-first-plan behavior is local options only).
|
|
115
|
+
public func capturePlan() -> ForgeCapturePlan? {
|
|
116
|
+
kernel.capturePlan()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public func addOnSendRequestListener(_ listener: @escaping (String, inout [String: String]) -> Void) -> ForgeEventSubscription {
|
|
120
|
+
kernel.addOnSendRequestListener(listener)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public func snapshotEvents() -> [ForgeRecordedEvent] {
|
|
124
|
+
kernel.snapshotEvents()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@objc public func setLogLevel(_ value: Any?) -> ForgeOutcome {
|
|
128
|
+
kernel.setLogLevel(ForgeLogLevel.from(value, default: kernel.effectiveLogLevel()))
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@objc public func startSession(_ metadata: [String: Any] = [:]) -> ForgeOutcome {
|
|
132
|
+
kernel.startSession(metadata: metadata.toStringMap())
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@objc public func endSession(_ metadata: [String: Any] = [:]) -> ForgeOutcome {
|
|
136
|
+
kernel.endSession(metadata: metadata.toStringMap())
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public func trackSessionNavigation(_ metadata: [String: Any]) -> ForgeOutcome {
|
|
140
|
+
kernel.trackSessionNavigation(metadata.toStringMap())
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public func trackSessionEvent(name: String, metadata: [String: Any] = [:]) -> ForgeOutcome {
|
|
144
|
+
kernel.trackSessionEvent(name: name, metadata: metadata.toStringMap())
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public func trackSessionEventStop(name: String, metadata: [String: Any] = [:]) -> ForgeOutcome {
|
|
148
|
+
kernel.trackSessionEventStop(name: name, metadata: metadata.toStringMap())
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
public func trackSessionError(errorCode: String, errorMessage: String, metadata: [String: Any] = [:]) -> ForgeOutcome {
|
|
152
|
+
kernel.trackSessionError(errorCode: errorCode, errorMessage: errorMessage, metadata: metadata.toStringMap())
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/// Emits a raw event: the name gains the `raw_` prefix when missing and
|
|
156
|
+
/// travels on the session-event path, matching the web binding shape.
|
|
157
|
+
public func trackRawEvent(name: String, params: [String: Any] = [:]) -> ForgeOutcome {
|
|
158
|
+
kernel.trackRawEvent(name: name, params: params.toStringMap())
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/// Emits a `raw_search` event with the query and optional result count.
|
|
162
|
+
public func trackSearch(query: String, resultCount: Int? = nil) -> ForgeOutcome {
|
|
163
|
+
kernel.trackSearch(query: query, resultCount: resultCount)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/// Emits a `raw_search_result_click` event for a result chosen from the
|
|
167
|
+
/// results produced by the query last reported via `trackSearch`.
|
|
168
|
+
public func trackSearchResultClick(query: String, options: ForgeSearchResultClickOptions = ForgeSearchResultClickOptions()) -> ForgeOutcome {
|
|
169
|
+
kernel.trackSearchResultClick(query: query, options: options)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// Emits a typed business raw event whose attributes travel under the
|
|
173
|
+
/// `target.attr.*` convention.
|
|
174
|
+
public func trackBusinessEvent(name: String, attributes: [String: Any] = [:]) -> ForgeOutcome {
|
|
175
|
+
kernel.trackBusinessEvent(name: name, attributes: attributes.toStringMap())
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/// Emits a `raw_attribution` event for apps that resolve their own
|
|
179
|
+
/// campaign parameters.
|
|
180
|
+
public func trackAttribution(options: ForgeAttributionOptions = ForgeAttributionOptions()) -> ForgeOutcome {
|
|
181
|
+
kernel.trackAttribution(options: options)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/// Reports a successful login; the kernel emits `login
|
|
185
|
+
/// {authStatus:"success"}` and atomically restarts the app session when
|
|
186
|
+
/// the effective identity changed.
|
|
187
|
+
public func loginSuccess(userId: String, options: [String: String] = [:]) -> ForgeOutcome {
|
|
188
|
+
kernel.loginSuccess(userId: userId, options: options)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/// Reports a failed login attempt: `login {authStatus:"failure"}`.
|
|
192
|
+
public func loginFailed(options: [String: String] = [:]) -> ForgeOutcome {
|
|
193
|
+
kernel.loginFailed(options: options)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/// Reports a logout; the kernel clears the identity to Anonymous and
|
|
197
|
+
/// atomically restarts the app session.
|
|
198
|
+
public func logout(options: [String: String] = [:]) -> ForgeOutcome {
|
|
199
|
+
kernel.logout(options: options)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// Reports a profile creation: `profile_created {profileId, profileType?}`.
|
|
203
|
+
public func profileCreated(profileId: String, options: [String: String] = [:]) -> ForgeOutcome {
|
|
204
|
+
kernel.profileCreated(profileId: profileId, options: options)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/// Reports a profile selection; the kernel stamps `profileId` on the
|
|
208
|
+
/// session identity and restarts the app session when it changed.
|
|
209
|
+
public func profileSelected(profileId: String, options: [String: String] = [:]) -> ForgeOutcome {
|
|
210
|
+
kernel.profileSelected(profileId: profileId, options: options)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/// Reports a profile deletion: `profile_deleted {profileId}`.
|
|
214
|
+
public func profileDeleted(profileId: String) -> ForgeOutcome {
|
|
215
|
+
kernel.profileDeleted(profileId: profileId)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
public func trackNetworkRequest(_ record: NetworkRequestRecord) -> ForgeOutcome {
|
|
219
|
+
guard !destroyed else { return .ignoredInvalidState }
|
|
220
|
+
return kernel.trackNetworkRequest(record)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
public func getVideoAdapter(identifier: String? = nil) -> VideoAdapter? {
|
|
224
|
+
let id = identifier?.nilIfBlank ?? Self.defaultVideoAdapterIdentifier
|
|
225
|
+
lock.lock()
|
|
226
|
+
defer { lock.unlock() }
|
|
227
|
+
return videoAdapters.first { $0.identifier == id && !$0.isDestroyed }
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
public func destroy() -> ForgeOutcome {
|
|
231
|
+
lock.lock()
|
|
232
|
+
defer { lock.unlock() }
|
|
233
|
+
guard !destroyed else { return .ignoredDuplicate }
|
|
234
|
+
destroyed = true
|
|
235
|
+
let adapters = videoAdapters
|
|
236
|
+
videoAdapters.removeAll()
|
|
237
|
+
adapters.forEach { _ = $0.destroy() }
|
|
238
|
+
return kernel.destroy()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
func buildVideoAdapter(
|
|
242
|
+
identifier: String,
|
|
243
|
+
options: VideoOptions,
|
|
244
|
+
playerAdapter: PlayerAdapter?,
|
|
245
|
+
adAdapter: AdAdapter?
|
|
246
|
+
) -> VideoAdapter {
|
|
247
|
+
lock.lock()
|
|
248
|
+
defer { lock.unlock() }
|
|
249
|
+
destroyAdapters(identifier: identifier)
|
|
250
|
+
destroyAdapters(using: playerAdapter, owner: nil)
|
|
251
|
+
destroyAdapters(using: adAdapter, owner: nil)
|
|
252
|
+
let adapter = VideoAdapter(forge: self, identifier: identifier, options: options)
|
|
253
|
+
videoAdapters.append(adapter)
|
|
254
|
+
_ = adapter.replacePlayerAdapter(playerAdapter, checkDuplicates: false)
|
|
255
|
+
_ = adapter.replaceAdAdapter(adAdapter, checkDuplicates: false)
|
|
256
|
+
return adapter
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
func onPlayerAdapterRegistered(owner: VideoAdapter, playerAdapter: PlayerAdapter?) {
|
|
260
|
+
lock.lock()
|
|
261
|
+
defer { lock.unlock() }
|
|
262
|
+
destroyAdapters(using: playerAdapter, owner: owner)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
func onAdAdapterRegistered(owner: VideoAdapter, adAdapter: AdAdapter?) {
|
|
266
|
+
lock.lock()
|
|
267
|
+
defer { lock.unlock() }
|
|
268
|
+
destroyAdapters(using: adAdapter, owner: owner)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
func onVideoAdapterDestroyed(_ adapter: VideoAdapter) {
|
|
272
|
+
lock.lock()
|
|
273
|
+
defer { lock.unlock() }
|
|
274
|
+
videoAdapters.removeAll { $0 === adapter }
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private func destroyAdapters(identifier: String) {
|
|
278
|
+
videoAdapters.filter { $0.identifier == identifier && !$0.isDestroyed }.forEach { _ = $0.destroy() }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private func destroyAdapters(using candidate: AnyObject?, owner: VideoAdapter?) {
|
|
282
|
+
guard let candidate else { return }
|
|
283
|
+
videoAdapters
|
|
284
|
+
.filter { $0 !== owner && !$0.isDestroyed && $0.usesAdapterOrPlayer(candidate) }
|
|
285
|
+
.forEach { _ = $0.destroy() }
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
public final class Builder {
|
|
289
|
+
private var accountCode: String
|
|
290
|
+
private var appName = Bundle.main.displayName ?? NPAWForge.defaultAppName
|
|
291
|
+
private var appReleaseVersion = Bundle.main.releaseVersion
|
|
292
|
+
private var buildVariant = NPAWForge.defaultBuildVariant
|
|
293
|
+
private var lmaEndpoint = NPAWForge.defaultLmaEndpoint
|
|
294
|
+
private var analyticsUserAgent: String?
|
|
295
|
+
private var allowLocalHttp = false
|
|
296
|
+
private var startSessionOnInitialize = false
|
|
297
|
+
private var processEventsBeforeSessionStart = false
|
|
298
|
+
private var preSessionEventQueueLimitEvents = 100
|
|
299
|
+
private var preSessionEventQueueMaxAgeMs: Int64 = 30_000
|
|
300
|
+
private var logLevel: ForgeLogLevel = .warning
|
|
301
|
+
private var services: ForgeAppleServices?
|
|
302
|
+
|
|
303
|
+
public init(accountCode: String) {
|
|
304
|
+
self.accountCode = accountCode
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
@available(*, deprecated, message: "pluginVersion is owned by the Forge SDK build and this setter is ignored.")
|
|
308
|
+
@discardableResult public func setPluginVersion(_ value: String?) -> Builder {
|
|
309
|
+
_ = value
|
|
310
|
+
return self
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
@discardableResult public func setAppName(_ value: String?) -> Builder {
|
|
314
|
+
appName = value?.nilIfBlank ?? appName
|
|
315
|
+
return self
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
@discardableResult public func setApplicationName(_ value: String?) -> Builder { setAppName(value) }
|
|
319
|
+
|
|
320
|
+
@discardableResult public func setAppReleaseVersion(_ value: String?) -> Builder {
|
|
321
|
+
appReleaseVersion = value?.nilIfBlank
|
|
322
|
+
return self
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
@discardableResult public func setAppVersion(_ value: String?) -> Builder { setAppReleaseVersion(value) }
|
|
326
|
+
@discardableResult public func setApplicationVersion(_ value: String?) -> Builder { setAppReleaseVersion(value) }
|
|
327
|
+
|
|
328
|
+
@discardableResult public func setLmaEndpoint(_ value: String?) -> Builder {
|
|
329
|
+
lmaEndpoint = value?.nilIfBlank ?? lmaEndpoint
|
|
330
|
+
return self
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
@discardableResult public func setAnalyticsHost(_ value: String?) -> Builder { setLmaEndpoint(value) }
|
|
334
|
+
@discardableResult public func setHost(_ value: String?) -> Builder { setLmaEndpoint(value) }
|
|
335
|
+
|
|
336
|
+
@discardableResult public func setAnalyticsUserAgent(_ value: String?) -> Builder {
|
|
337
|
+
analyticsUserAgent = value?.nilIfBlank
|
|
338
|
+
return self
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
@discardableResult public func setLocalPlaintextTransportForTesting(_ enabled: Bool) -> Builder {
|
|
342
|
+
allowLocalHttp = enabled
|
|
343
|
+
return self
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@discardableResult public func setStartSessionOnInitialize(_ enabled: Bool) -> Builder {
|
|
347
|
+
startSessionOnInitialize = enabled
|
|
348
|
+
return self
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
@discardableResult public func setProcessEventsBeforeSessionStart(_ enabled: Bool) -> Builder {
|
|
352
|
+
processEventsBeforeSessionStart = enabled
|
|
353
|
+
return self
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
@discardableResult public func setPreSessionEventQueueLimitEvents(_ limit: Int) -> Builder {
|
|
357
|
+
preSessionEventQueueLimitEvents = max(0, limit)
|
|
358
|
+
return self
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
@discardableResult public func setPreSessionEventQueueMaxAgeMs(_ maxAgeMs: Int64) -> Builder {
|
|
362
|
+
preSessionEventQueueMaxAgeMs = max(0, maxAgeMs)
|
|
363
|
+
return self
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
@discardableResult public func setLogLevel(_ value: Any?) -> Builder {
|
|
367
|
+
logLevel = ForgeLogLevel.from(value)
|
|
368
|
+
return self
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
@discardableResult public func setBuildVariant(_ value: String?) -> Builder {
|
|
372
|
+
buildVariant = value?.nilIfBlank ?? buildVariant
|
|
373
|
+
return self
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
@discardableResult public func setServices(_ value: ForgeAppleServices?) -> Builder {
|
|
377
|
+
services = value
|
|
378
|
+
return self
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
public func build() -> NPAWForge {
|
|
382
|
+
let config = ForgeAppleConfig(
|
|
383
|
+
accountCode: accountCode,
|
|
384
|
+
pluginVersion: NPAWForge.defaultPluginVersion,
|
|
385
|
+
appName: appName,
|
|
386
|
+
buildVariant: buildVariant,
|
|
387
|
+
appReleaseVersion: appReleaseVersion,
|
|
388
|
+
lmaEndpoint: lmaEndpoint,
|
|
389
|
+
analyticsUserAgent: analyticsUserAgent,
|
|
390
|
+
allowLocalHttp: allowLocalHttp,
|
|
391
|
+
startSessionOnInitialize: startSessionOnInitialize,
|
|
392
|
+
processEventsBeforeSessionStart: processEventsBeforeSessionStart,
|
|
393
|
+
preSessionEventQueueLimitEvents: preSessionEventQueueLimitEvents,
|
|
394
|
+
preSessionEventQueueMaxAgeMs: preSessionEventQueueMaxAgeMs,
|
|
395
|
+
logLevel: logLevel
|
|
396
|
+
)
|
|
397
|
+
let resolvedServices = services ?? ForgeAppleServices(storage: UserDefaultsStorage())
|
|
398
|
+
let forge = NPAWForge(kernel: ForgeApple(config: config, services: resolvedServices))
|
|
399
|
+
ForgeAutomaticCaptureRegistry.installIfAvailable(forge)
|
|
400
|
+
return forge
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
public enum ForgeAutomaticCaptureRegistry {
|
|
406
|
+
private static let lock = NSLock()
|
|
407
|
+
private static var installers: [(NPAWForge) -> Void] = []
|
|
408
|
+
|
|
409
|
+
public static func register(_ installer: @escaping (NPAWForge) -> Void) {
|
|
410
|
+
lock.lock()
|
|
411
|
+
defer { lock.unlock() }
|
|
412
|
+
installers.append(installer)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
static func installIfAvailable(_ forge: NPAWForge) {
|
|
416
|
+
lock.lock()
|
|
417
|
+
let snapshot = installers
|
|
418
|
+
lock.unlock()
|
|
419
|
+
snapshot.forEach { $0(forge) }
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
private extension Dictionary where Key == String, Value == Any {
|
|
424
|
+
func toStringMap() -> [String: String] {
|
|
425
|
+
reduce(into: [String: String]()) { output, element in
|
|
426
|
+
let key = element.key.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
427
|
+
guard !key.isEmpty else { return }
|
|
428
|
+
let value = String(describing: element.value)
|
|
429
|
+
guard !value.isEmpty else { return }
|
|
430
|
+
output[key] = value
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
private extension String {
|
|
436
|
+
var nilIfBlank: String? {
|
|
437
|
+
let trimmed = trimmingCharacters(in: .whitespacesAndNewlines)
|
|
438
|
+
return trimmed.isEmpty ? nil : trimmed
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
private extension Bundle {
|
|
443
|
+
var displayName: String? {
|
|
444
|
+
object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
|
|
445
|
+
?? object(forInfoDictionaryKey: "CFBundleName") as? String
|
|
446
|
+
?? bundleIdentifier
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
var releaseVersion: String? {
|
|
450
|
+
object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
|
|
451
|
+
?? object(forInfoDictionaryKey: "CFBundleVersion") as? String
|
|
452
|
+
}
|
|
453
|
+
}
|