Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

iOS: Expose builldBeacon and cancelBeacon hooks inside of BaseBeaconPlugin and make BeaconPlugin open class #536

Merged
merged 14 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 30 additions & 30 deletions ios/core/Sources/Types/Hooks/Hook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@
/// A base for implementing JS backed hooks
open class BaseJSHook {
private let baseValue: JSValue

/// The JS reference to the hook
public var hook: JSValue { baseValue.objectForKeyedSubscript("hooks").objectForKeyedSubscript(name) }

/// The JSContext for the hook
public var context: JSContext { hook.context }

/// The name of the hook
public let name: String

/// Retrieves a hook by name from an object in JS
/// - Parameters:
/// - baseValue: The object that has `hooks`
Expand All @@ -39,7 +39,7 @@
/**
Attach a closure to the hook, so when the hook is fired in the JS runtime
we receive the event in the native runtime

- parameters:
- hook: A function to run when the JS hook is fired
*/
Expand All @@ -51,7 +51,7 @@
else { return }
hook(hookValue)
}

self.hook.invokeMethod("tap", withArguments: [name, JSValue(object: tapMethod, in: context) as Any])
}
}
Expand All @@ -64,21 +64,21 @@
/**
Attach a closure to the hook, so when the hook is fired in the JS runtime
we receive the event in the native runtime

- parameters:
- hook: A function to run when the JS hook is fired
*/
public func tap(_ hook: @escaping (T, U) -> Void) {
let tapMethod: @convention(block) (JSValue?, JSValue?) -> Void = { value, value2 in
public func tap<R>(_ hook: @escaping (T, U) -> R) {
let tapMethod: @convention(block) (JSValue?, JSValue?) -> Any? = { value, value2 in
guard
let val = value,
let val2 = value2,
let hookValue = T.createInstance(value: val) as? T,
let hookValue2 = U.createInstance(value: val2) as? U
else { return }
hook(hookValue, hookValue2)
else { return nil }
return hook(hookValue, hookValue2)
}

self.hook.invokeMethod("tap", withArguments: [name, JSValue(object: tapMethod, in: context) as Any])
}
}
Expand All @@ -91,7 +91,7 @@
/**
Attach a closure to the hook, so when the hook is fired in the JS runtime
we receive the event in the native runtime

- parameters:
- hook: A function to run when the JS hook is fired
*/
Expand All @@ -103,7 +103,7 @@
else { return }
hook(hookValue)
}

self.hook.invokeMethod("tap", withArguments: [name, JSValue(object: tapMethod, in: context) as Any])
}
}
Expand All @@ -116,13 +116,13 @@
/**
Attach a closure to the hook, so when the hook is fired in the JS runtime
we receive the event in the native runtime

- parameters:
- hook: A function to run when the JS hook is fired
*/
public func tap(_ hook: @escaping (T, U) -> Void) {
let tapMethod: @convention(block) (JSValue?, JSValue?) -> Void = { value, value2 in

Check warning on line 125 in ios/core/Sources/Types/Hooks/Hook.swift

View check run for this annotation

Codecov / codecov/patch

ios/core/Sources/Types/Hooks/Hook.swift#L125

Added line #L125 was not covered by tests
let decoder = JSONDecoder()
guard
let val = value,
Expand All @@ -132,7 +132,7 @@
else { return }
hook(hookValue, hookValue2)
}

Check warning on line 135 in ios/core/Sources/Types/Hooks/Hook.swift

View check run for this annotation

Codecov / codecov/patch

ios/core/Sources/Types/Hooks/Hook.swift#L135

Added line #L135 was not covered by tests
self.hook.invokeMethod("tap", withArguments: [name, JSValue(object: tapMethod, in: context) as Any])
}
}
Expand All @@ -143,13 +143,13 @@
*/
public class AsyncHook<T>: BaseJSHook where T: CreatedFromJSValue {
private var handler: AsyncHookHandler?

public typealias AsyncHookHandler = (T) async throws -> JSValue?

/**
Attach a closure to the hook, so when the hook is fired in the JS runtime
we receive the event in the native runtime

- parameters:
- hook: A function to run when the JS hook is fired
*/
Expand All @@ -159,7 +159,7 @@
let val = value,
let hookValue = T.createInstance(value: val) as? T
else { return JSValue() }

Check warning on line 162 in ios/core/Sources/Types/Hooks/Hook.swift

View check run for this annotation

Codecov / codecov/patch

ios/core/Sources/Types/Hooks/Hook.swift#L162

Added line #L162 was not covered by tests
let promise =
JSUtilities.createPromise(context: self.context, handler: { (resolve, _) in
Task {
Expand All @@ -169,10 +169,10 @@
}
}
})

Check warning on line 172 in ios/core/Sources/Types/Hooks/Hook.swift

View check run for this annotation

Codecov / codecov/patch

ios/core/Sources/Types/Hooks/Hook.swift#L172

Added line #L172 was not covered by tests
return promise ?? JSValue()
}

Check warning on line 175 in ios/core/Sources/Types/Hooks/Hook.swift

View check run for this annotation

Codecov / codecov/patch

ios/core/Sources/Types/Hooks/Hook.swift#L175

Added line #L175 was not covered by tests
self.hook.invokeMethod("tap", withArguments: [name, JSValue(object: tapMethod, in: context) as Any])
}
}
Expand All @@ -184,13 +184,13 @@
*/
public class AsyncHook2<T, U>: BaseJSHook where T: CreatedFromJSValue, U: CreatedFromJSValue {
private var handler: AsyncHookHandler?

public typealias AsyncHookHandler = (T, U) async throws -> JSValue?

/**
Attach a closure to the hook, so when the hook is fired in the JS runtime
we receive the event in the native runtime

- parameters:
- hook: A function to run when the JS hook is fired
*/
Expand All @@ -202,8 +202,8 @@
let hookValue = T.createInstance(value: val) as? T,
let hookValue2 = U.createInstance(value: val2) as? U
else { return JSValue() }


let promise =
JSUtilities.createPromise(context: self.context, handler: { (resolve, _) in
Task {
Expand All @@ -213,10 +213,10 @@
}
}
})

return promise ?? JSValue()
}

self.hook.invokeMethod("tap", withArguments: [name, JSValue(object: tapMethod, in: context) as Any])
}
}
Expand Down
57 changes: 40 additions & 17 deletions plugins/beacon/ios/Sources/BaseBeaconPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@
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
Expand All @@ -52,37 +52,50 @@
Used as a base for framework specific integrations
*/
open class BaseBeaconPlugin<BeaconStruct: Decodable>: JSBasePlugin {

public var hooks: BeaconPluginHooks?
/// 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
- 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: "BeaconPlugin.native", pluginName: "BeaconPlugin.BeaconPlugin")
self.callback = onBeacon
self.plugins = plugins
}


override open func setup(context: JSContext) {
super.setup(context: context)
guard let pluginRef = self.pluginRef else {
fatalError("pluginRef is nil after setup")

Check warning on line 78 in plugins/beacon/ios/Sources/BaseBeaconPlugin.swift

View check run for this annotation

Codecov / codecov/patch

plugins/beacon/ios/Sources/BaseBeaconPlugin.swift#L78

Added line #L78 was not covered by tests
}
self.hooks = BeaconPluginHooks(
buildBeacon: AsyncHook2(baseValue: pluginRef, name: "buildBeacon"),
cancelBeacon: Hook2(baseValue: pluginRef, name: "cancelBeacon")
)
}

override open func getUrlForFile(fileName: String) -> URL? {
#if SWIFT_PACKAGE
#if SWIFT_PACKAGE
ResourceUtilities.urlForFile(name: fileName, ext: "js", bundle: Bundle.module)
#else
#else
ResourceUtilities.urlForFile(
name: fileName,
ext: "js",
bundle: Bundle(for: BaseBeaconPlugin<DefaultBeacon>.self),
pathComponent: "PlayerUI_BaseBeaconPlugin.bundle"
)
#endif
#endif
}

/**
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
Expand All @@ -105,12 +118,12 @@
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
- action: The action that was taken for the beacon
- args: The context of the beacon
*/
public func beacon(assetBeacon: AssetBeacon) {
guard
Expand All @@ -120,4 +133,14 @@
else { return }
pluginRef?.invokeMethod("beacon", withArguments: [beaconObject])
}

public struct BeaconPluginHooks {
public let buildBeacon: AsyncHook2<JSValue, JSValue>
public let cancelBeacon: Hook2<JSValue, JSValue>

public init(buildBeacon: AsyncHook2<JSValue, JSValue>, cancelBeacon: Hook2<JSValue, JSValue>) {
self.buildBeacon = buildBeacon
self.cancelBeacon = cancelBeacon
}
}
}
Loading