/
BaseBeaconPlugin.swift
115 lines (102 loc) · 3.97 KB
/
BaseBeaconPlugin.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//
// BaseBeaconPlugin.swift
// PlayerUI
//
// Created by Borawski, Harris on 3/11/21.
//
import Foundation
import JavaScriptCore
/**
Represenation of a Beacon coming from Player
*/
public struct DefaultBeacon: Codable, Hashable {
/// The action the user performed
public let action: String
/// The element that performed the action
public let element: String
/// The ID of the asset triggering the beacon
public let assetId: String?
/// The ID of the view triggering the beacon
public let viewId: String?
/// Additional data added from the asset or metaData
public let data: AnyType?
/// Construct a ``DefaultBeacon``
/// - Parameters:
/// - action: The action the user performed
/// - element: The element that performed the action
/// - assetId: The ID of the asset triggering the beacon
/// - viewId: The ID of the view triggering the beacon
/// - data: Additional data added from the asset or metaData
public init(action: String, element: String, assetId: String?, viewId: String?, data: AnyType?) {
self.action = action
self.element = element
self.assetId = assetId
self.viewId = viewId
self.data = data
}
}
/**
A Base implementation wrapping @player-ui/beacon-plugin
Used as a base for framework specific integrations
*/
open class BaseBeaconPlugin<BeaconStruct: Decodable>: JSBasePlugin {
/// The callback to call when a beacon is fired from the plugin
public var callback: ((BeaconStruct) -> Void)?
/// BeaconPluginPlugin's to use for this BeaconPlugin
public var plugins: [JSBasePlugin] = []
/**
Constructs a BeaconPlugin
- parameters:
- context: The context to load the plugin into
- onBeacon: A callback to receive beacon events
*/
public convenience init(plugins: [JSBasePlugin] = [], onBeacon: ((BeaconStruct) -> Void)?) {
self.init(fileName: "beacon-plugin.prod", pluginName: "BeaconPlugin.BeaconPlugin")
self.callback = onBeacon
self.plugins = plugins
}
override open func getUrlForFile(fileName: String) -> URL? {
ResourceUtilities.urlForFile(
name: fileName,
ext: "js",
bundle: Bundle(for: BaseBeaconPlugin<DefaultBeacon>.self),
pathComponent: "PlayerUI_BaseBeaconPlugin.bundle"
)
}
/**
Retrieves the arguments for constructing this plugin, this is necessary because the arguments need to be supplied after
construction of the swift object, once the context has been provided
- returns: An array of arguments to construct the plugin
*/
override public func getArguments() -> [Any] {
for plugin in plugins {
plugin.context = self.context
}
let callback: @convention(block) (JSValue?) -> Void = { [weak self] rawBeacon in
guard
let object = rawBeacon?.toObject(),
let data = try? JSONSerialization.data(withJSONObject: object),
let beacon = try? AnyTypeDecodingContext(rawData: data)
.inject(to: JSONDecoder())
.decode(BeaconStruct.self, from: data)
else { return }
self?.callback?(beacon)
}
let jsCallback = JSValue(object: callback, in: context) as Any
return [["callback": jsCallback, "plugins": plugins.map { $0.pluginRef }]]
}
/**
Function to send a beacon event through the plugin for processing
- parameters:
- action: The action that was taken for the beacon
- args: The context of the beacon
*/
public func beacon(assetBeacon: AssetBeacon) {
guard
let beacon = try? JSONEncoder().encode(assetBeacon),
let beaconString = String(data: beacon, encoding: .utf8),
let beaconObject = pluginRef?.context.evaluateScript("(\(beaconString))")
else { return }
pluginRef?.invokeMethod("beacon", withArguments: [beaconObject])
}
}