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

jvm and ios-async-node-ability-to-remove-resolved-async-node #488

Merged
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2f28724
Initial code commit
sakuntala-motukuri Aug 12, 2024
dcc3187
Merge branch 'main' into jvm-async-node-ability-to-remove-resolved-as…
sakuntala-motukuri Aug 12, 2024
477c0c6
jvm-async-node-ability-to-remove-resolved-async-node
sakuntala-motukuri Aug 13, 2024
8fd4dab
fixed linter
sakuntala-motukuri Aug 13, 2024
d865fb3
ios-async-node-ability-to-remove-resolved-async-node
sakuntala-motukuri Aug 13, 2024
f78df38
changed some logic and reverted NodeSerializableField
sakuntala-motukuri Aug 14, 2024
0340188
ios expose constantsController #446
cehan-Chloe Aug 9, 2024
2425424
add docstrings to public funcs
cehan-Chloe Aug 12, 2024
729929f
fix return type and add tests
cehan-Chloe Aug 12, 2024
da38906
add comments
cehan-Chloe Aug 14, 2024
bceb781
Android/JVM - expose constantController
cehan-Chloe Aug 14, 2024
850bf52
fix test build
cehan-Chloe Aug 14, 2024
f55e495
remove start player in tests
cehan-Chloe Aug 14, 2024
a2c46cc
fix test build
cehan-Chloe Aug 14, 2024
2a63f45
Changed logic and updated test cases for android
sakuntala-motukuri Aug 17, 2024
5bcdcb3
Merge branch 'main' into jvm-async-node-ability-to-remove-resolved-as…
sakuntala-motukuri Aug 17, 2024
6917491
Updated 'handle multiple updates through callback mechanism'
sakuntala-motukuri Aug 19, 2024
de6df57
updated async-node ios version and fixed review comments
sakuntala-motukuri Aug 21, 2024
d252411
Fixed ios tests
sakuntala-motukuri Aug 21, 2024
04f3573
Removed callback from testHandleMultipleUpdatesThroughCallback
sakuntala-motukuri Aug 22, 2024
663ce13
Merge branch 'main' into jvm-async-node-ability-to-remove-resolved-as…
sakuntala-motukuri Aug 22, 2024
91359bc
Fixed review comments and updated ios test cases
sakuntala-motukuri Aug 22, 2024
a9542b4
updated test to .emptyNode case
sakuntala-motukuri Aug 22, 2024
f864330
added assertions in ios
sakuntala-motukuri Aug 23, 2024
45fe9fc
added suspend in jvm
sakuntala-motukuri Aug 23, 2024
ad6d7b8
Fixed review comments for null node test and callback test
sakuntala-motukuri Aug 26, 2024
428d005
Linter fix
sakuntala-motukuri Aug 26, 2024
ace8b73
Updated review comments
sakuntala-motukuri Aug 26, 2024
e8dc375
Added doc comment
sakuntala-motukuri Aug 26, 2024
5b850e0
updated ios review comments and updated tests
sakuntala-motukuri Sep 3, 2024
df5c4d6
Merge branch 'main' into jvm-async-node-ability-to-remove-resolved-as…
sakuntala-motukuri Sep 3, 2024
32b9c57
removed white spaces
sakuntala-motukuri Sep 3, 2024
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
44 changes: 44 additions & 0 deletions ios/core/Sources/Types/Hooks/Hook.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,47 @@ public class AsyncHook<T>: BaseJSHook where T: CreatedFromJSValue {
self.hook.invokeMethod("tap", withArguments: [name, JSValue(object: tapMethod, in: context) as Any])
}
}

/**
This class represents an object in the JS runtime that can be tapped into
and returns a promise that resolves when the asynchronous task is completed
*/
sakuntala-motukuri marked this conversation as resolved.
Show resolved Hide resolved
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
*/
public func tap(_ hook: @escaping AsyncHookHandler) {
let tapMethod: @convention(block) (JSValue?,JSValue?) -> JSValue = { 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 JSValue() }


let promise =
JSUtilities.createPromise(context: self.context, handler: { (resolve, _) in
Task {
let result = try await hook(hookValue, hookValue2)
DispatchQueue.main.async {
resolve(result as Any)
}
}
})

return promise ?? JSValue()
}

self.hook.invokeMethod("tap", withArguments: [name, JSValue(object: tapMethod, in: context) as Any])
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public val Runtime<*>.Promise: Promise.Api get() = getObject("Promise")?.let { p
} ?: throw PlayerRuntimeException("'Promise' not defined in runtime")

/** Helper to bridge complex [Promise] logic with the JS promise constructor */
public fun <T : Any> Runtime<*>.Promise(block: suspend ((T) -> Unit, (Throwable) -> Unit) -> Unit): Promise {
public fun <T : Any?> Runtime<*>.Promise(block: suspend ((T) -> Unit, (Throwable) -> Unit) -> Unit): Promise {
sakuntala-motukuri marked this conversation as resolved.
Show resolved Hide resolved
val key = "promiseHandler_${UUID.randomUUID().toString().replace("-", "")}"
add(key) { resolve: Invokable<Any?>, reject: Invokable<Any?> ->
runtime.scope.launch {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.intuit.playerui.core.bridge.hooks

import com.intuit.hooks.AsyncParallelBailHook
import com.intuit.hooks.BailResult
import com.intuit.hooks.HookContext
import com.intuit.playerui.core.bridge.Node
import com.intuit.playerui.core.bridge.serialization.serializers.NodeWrapperSerializer
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.serialization.KSerializer
import kotlinx.serialization.Serializable

@OptIn(ExperimentalCoroutinesApi::class)
@Serializable(with = NodeAsyncParallelBailHook2.Serializer::class)
public class NodeAsyncParallelBailHook2<T1, T2, R : Any?>(
override val node: Node,
serializer1: KSerializer<T1>,
serializer2: KSerializer<T2>,
) : AsyncParallelBailHook<suspend (HookContext, T1, T2) -> BailResult<R>, R>(), AsyncNodeHook<R> {

init {
init(serializer1, serializer2)
}

override suspend fun callAsync(context: HookContext, serializedArgs: Array<Any?>): R {
require(serializedArgs.size == 2) { "Expected exactly two arguments, but got ${serializedArgs.size}" }
val (p1, p2) = serializedArgs
val result = call(10) { f, _ ->
f(context, p1 as T1, p2 as T2)
} as R
return result
}

internal class Serializer<T1, T2, R : Any>(
private val serializer1: KSerializer<T1>,
private val serializer2: KSerializer<T2>,
`_`: KSerializer<R>,
) : NodeWrapperSerializer<NodeAsyncParallelBailHook2<T1, T2, R>>({
NodeAsyncParallelBailHook2(it, serializer1, serializer2)
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ internal interface NodeHook<R> : NodeWrapper {
fun call(context: HookContext, serializedArgs: Array<Any?>): R
}

internal interface AsyncNodeHook<R : Any> : NodeHook<Promise> {
internal interface AsyncNodeHook<R : Any?> : NodeHook<Promise> {
override fun call(context: HookContext, serializedArgs: Array<Any?>): Promise = node.runtime.Promise { resolve, reject ->
val result = callAsync(context, serializedArgs)
resolve(result)
Expand Down
93 changes: 48 additions & 45 deletions plugins/async-node/ios/Sources/AsyncNodePlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@

import Foundation
import JavaScriptCore

#if SWIFT_PACKAGE
import PlayerUI
#endif

public typealias AsyncHookHandler = (JSValue) async throws -> AsyncNodeHandlerType
public typealias AsyncHookHandler = (JSValue, JSValue) async throws -> AsyncNodeHandlerType

public enum AsyncNodeHandlerType {
case multiNode([ReplacementNode])
case singleNode(ReplacementNode)
case emptyNode
}

/**
Expand All @@ -42,57 +40,62 @@
self.plugins = plugins
}

override public func setup(context: JSContext) {
super.setup(context: context)

if let pluginRef = pluginRef {
self.hooks = AsyncNodeHook(onAsyncNode: AsyncHook(baseValue: pluginRef, name: "onAsyncNode"))
}

hooks?.onAsyncNode.tap({ node in
// hook value is the original node
guard let asyncHookHandler = self.asyncHookHandler else {
return JSValue()
}

let replacementNode = try await (asyncHookHandler)(node)

switch replacementNode {
case .multiNode(let replacementNodes):
let jsValueArray = replacementNodes.compactMap({ node in
switch node {
case .concrete(let jsValue):
return jsValue
case .encodable(let encodable):
let encoder = JSONEncoder()
do {
let res = try encoder.encode(encodable)
return context.evaluateScript("(\(String(data: res, encoding: .utf8) ?? ""))") as JSValue
} catch {
return nil
}
}
})

return context.objectForKeyedSubscript("Array").objectForKeyedSubscript("from").call(withArguments: [jsValueArray])

case .singleNode(let replacementNode):
switch replacementNode {

func handleAsyncNodeReplacement(_ replacementNode: AsyncNodeHandlerType) -> JSValue? {
sakuntala-motukuri marked this conversation as resolved.
Show resolved Hide resolved
guard let context = context else {
return JSValue()

Check warning on line 45 in plugins/async-node/ios/Sources/AsyncNodePlugin.swift

View check run for this annotation

Codecov / codecov/patch

plugins/async-node/ios/Sources/AsyncNodePlugin.swift#L45

Added line #L45 was not covered by tests
}
switch replacementNode {
case .multiNode(let replacementNodes):
let jsValueArray = replacementNodes.compactMap { node in
switch node {
case .concrete(let jsValue):
return jsValue
case .encodable(let encodable):
let encoder = JSONEncoder()
do {
let res = try encoder.encode(encodable)
return context.evaluateScript("(\(String(data: res, encoding: .utf8) ?? ""))") as JSValue
} catch {
break
return nil

Check warning on line 59 in plugins/async-node/ios/Sources/AsyncNodePlugin.swift

View check run for this annotation

Codecov / codecov/patch

plugins/async-node/ios/Sources/AsyncNodePlugin.swift#L59

Added line #L59 was not covered by tests
}
case .concrete(let jsValue):
return jsValue
}
}
return context.objectForKeyedSubscript("Array").objectForKeyedSubscript("from").call(withArguments: [jsValueArray])

case .singleNode(let replacementNode):
switch replacementNode {
case .encodable(let encodable):
let encoder = JSONEncoder()
do {
let res = try encoder.encode(encodable)
return context.evaluateScript("(\(String(data: res, encoding: .utf8) ?? ""))") as JSValue
} catch {
return nil

Check warning on line 73 in plugins/async-node/ios/Sources/AsyncNodePlugin.swift

View check run for this annotation

Codecov / codecov/patch

plugins/async-node/ios/Sources/AsyncNodePlugin.swift#L73

Added line #L73 was not covered by tests
}
case .concrete(let jsValue):
return jsValue
}
sakuntala-motukuri marked this conversation as resolved.
Show resolved Hide resolved

case .emptyNode:
return nil
}
sakuntala-motukuri marked this conversation as resolved.
Show resolved Hide resolved
}

Check warning on line 82 in plugins/async-node/ios/Sources/AsyncNodePlugin.swift

View check run for this annotation

Codecov / codecov/patch

plugins/async-node/ios/Sources/AsyncNodePlugin.swift#L82

Added line #L82 was not covered by tests

override public func setup(context: JSContext) {
super.setup(context: context)

if let pluginRef = pluginRef {
self.hooks = AsyncNodeHook(onAsyncNode: AsyncHook2(baseValue: pluginRef, name: "onAsyncNode"))
}

hooks?.onAsyncNode.tap({ node, callback in
// hook value is the original node
guard let asyncHookHandler = self.asyncHookHandler else {
return JSValue()

Check warning on line 94 in plugins/async-node/ios/Sources/AsyncNodePlugin.swift

View check run for this annotation

Codecov / codecov/patch

plugins/async-node/ios/Sources/AsyncNodePlugin.swift#L94

Added line #L94 was not covered by tests
}

let replacementNode = try await (asyncHookHandler)(node, callback)
return self.handleAsyncNodeReplacement(replacementNode) ?? JSValue()
})
}

Expand Down Expand Up @@ -124,7 +127,7 @@
}

public struct AsyncNodeHook {
public let onAsyncNode: AsyncHook<JSValue>
public let onAsyncNode: AsyncHook2<JSValue, JSValue>
}

/**
Expand Down
Loading