Skip to content

Commit

Permalink
[iOS] relax requirements for sending beacon metadata (#88)
Browse files Browse the repository at this point in the history
relax requirements for sending beacon metadata
  • Loading branch information
hborawski authored Oct 20, 2022
1 parent 3b8d937 commit 92ff004
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 7 deletions.
27 changes: 24 additions & 3 deletions ios/packages/core/Sources/Types/Beacon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ public struct AssetBeacon: Codable {
}
}

/// MetaData requirements for `BaseBeaconPlugin` and its extensions
public protocol BeaconableMetaData {
/// Additional data to include when beaconing this asset
var beacon: AnyType? { get set }
}

/// Container Object for matching the data type for the JS Beacon Plugin
public struct BeaconableAsset: Codable {
/// The ID of the asset that fired the beacon
Expand All @@ -58,17 +64,32 @@ public struct BeaconableAsset: Codable {
/// - id: The ID of the asset that fired the beacon
/// - type: The type of the asset that fired the beacon
/// - metaData: Beacon applicable metaData from the asset that fired the beacon
public init(
public init<MetaDataType: BeaconableMetaData>(
id: String,
type: String? = nil,
metaData: MetaData? = nil
metaData: MetaDataType? = nil
) {
self.id = id
self.type = type
self.metaData = metaData
self.metaData = MetaData(beacon: metaData?.beacon)
}

/// Constructs a BeaconableAsset
/// - Parameters:
/// - id: The ID of the asset that fired the beacon
/// - type: The type of the asset that fired the beacon
public init(
id: String,
type: String? = nil
) {
self.id = id
self.type = type
self.metaData = nil
}
}

extension MetaData: BeaconableMetaData {}

/**
All potential Beacon Element types
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ struct ActionData: AssetData {
var label: WrappedAsset?
/// A function to run in the core when this action is invoked
var run: WrappedFunction<Void>?

/// Additional metaData for beaconing
var metaData: MetaData?
}

/**
Expand Down Expand Up @@ -40,7 +43,7 @@ struct ActionAssetView: View {
var body: some View {
Button(
action: {
beaconContext?.beacon(action: "clicked", element: "button", id: model.data.id)
beaconContext?.beacon(action: "clicked", element: "button", id: model.data.id, metaData: model.data.metaData)
self.model.data.run?()
},
label: {
Expand Down
37 changes: 35 additions & 2 deletions ios/plugins/BeaconPlugin/Sources/SwiftUIBeaconPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,47 @@ public class BeaconContext: ObservableObject {
- action: The type of action that occurred
- element: The type of element in the asset that triggered the beacon
- id: The ID of the asset that triggered the beacon
- metaData: `BeaconableMetaData` to include as extra data to the core BeaconPlugin
- data: Additional arbitrary data to include in the beacon
*/
public func beacon(action: String, element: String, id: String, type: String? = nil, metaData: MetaData? = nil, data: AnyType? = nil) {
public func beacon<MetaDataType: BeaconableMetaData>(
action: String,
element: String,
id: String,
type: String? = nil,
metaData: MetaDataType? = nil,
data: AnyType? = nil
) {
self.beaconFn(
AssetBeacon(
action: action,
element: element,
asset: BeaconableAsset(id: id, type: type, metaData: metaData),
asset: BeaconableAsset(id: id, type: type, metaData: metaData.map { MetaData(beacon: $0.beacon) }),
data: data
)
)
}

/**
Sends a beacon through the JavaScript beacon plugin
- parameters:
- action: The type of action that occurred
- element: The type of element in the asset that triggered the beacon
- id: The ID of the asset that triggered the beacon
- data: Additional arbitrary data to include in the beacon
*/
public func beacon(
action: String,
element: String,
id: String,
type: String? = nil,
data: AnyType? = nil
) {
self.beaconFn(
AssetBeacon(
action: action,
element: element,
asset: BeaconableAsset(id: id, type: type),
data: data
)
)
Expand Down
33 changes: 32 additions & 1 deletion ios/plugins/BeaconPlugin/ViewInspector/BeaconPluginTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,32 @@ class BeaconPluginTests: XCTestCase {
wait(for: [exp, expect], timeout: 10)
}

func testBeaconContextWithMetaData() throws {
let expect = expectation(description: "Beacon Called")
let beacon: (AssetBeacon) -> Void = { (beaconObj: AssetBeacon) in
guard
beaconObj.action == "clicked",
beaconObj.element == "button",
beaconObj.asset.id == "test",
case .dictionary(let data) = beaconObj.asset.metaData?.beacon,
data["field"] == "value"
else { return XCTFail("incorrect beacon information") }
expect.fulfill()
}

let context = BeaconContext(beacon)
let data = MetaData(beacon: .dictionary(data: ["field": "value"]))
var tree = TestButton(metaData: data)

let exp = tree.on(\.didAppear) { view in
try view.button().tap()
}

ViewHosting.host(view: tree.environment(\.beaconContext, context))

wait(for: [exp, expect], timeout: 10)
}

func testSendsViewBeacon() {
let beaconed = expectation(description: "View beacon called")
let plugin = BeaconPlugin<DefaultBeacon>(plugins: []) { (beacon) in
Expand All @@ -65,10 +91,15 @@ class BeaconPluginTests: XCTestCase {
extension TestButton: Inspectable {}
struct TestButton: View {
@Environment(\.beaconContext) var beaconContext
var metaData: MetaData?
internal var didAppear: ((Self) -> Void)?
var body: some View {
Button(action: {
beaconContext?.beacon(action: "clicked", element: "button", id: "test")
if let data = metaData {
beaconContext?.beacon(action: "clicked", element: "button", id: "test", metaData: data)
} else {
beaconContext?.beacon(action: "clicked", element: "button", id: "test")
}
}, label: {Text("Beacon")})
.onAppear { self.didAppear?(self) }
}
Expand Down

0 comments on commit 92ff004

Please sign in to comment.