从 Java Script (TVML) 调用带有完成处理程序的 Swift 函数

Posted

技术标签:

【中文标题】从 Java Script (TVML) 调用带有完成处理程序的 Swift 函数【英文标题】:Call a Swift function with completion handler from Java Script (TVML) 【发布时间】:2017-07-21 10:13:54 【问题描述】:

我正在开发 tvOS 应用程序(使用 TVML),我正在尝试使用来自 JS 的完成处理程序调用 Swift 函数。

在 TVApplicationControllerDelegate 中,我尝试使用如下代码:

let getVastData : @convention(block) (String,  @escaping (UInt32, [String], String) -> ()) -> Void = 
    (url : String, _ completion:@escaping (_ duration: UInt32, _ addLinks: [String], _ videoLink: String) -> ()) -> Void in
    VastManager.shared.startParsing(with: url, completion)


jsContext.setObject(unsafeBitCast(getVastData, to: AnyObject.self), forKeyedSubscript: "getVastData" as (NSCopying & NSObjectProtocol)!)

但我收到错误:“TypeError: getVastData 不是函数。(在 'getVastData' 中,'getVastData' 是 NSBlock 的实例”

你知道我错在哪里或者如何使用 JS 的完成处理程序更正调用 Swift func 吗?

提前致谢, 米洛斯

【问题讨论】:

【参考方案1】:

首先,我们需要在 jsContext 中注册我们的 Swift 方法。我们的 Swift 方法需要接受回调函数,在我们的例子中是对 JavaScipt 值的引用,该值将在我们在 Swift 中的操作完成后调用。

let getVastData : @convention(block) (String, JSValue) -> Void = 
    (url : String, completion : JSValue) -> Void in
    VastManager.shared.startParsing(from: url, completion: completion)

jsContext.setObject(unsafeBitCast(getVastData, to: AnyObject.self), forKeyedSubscript: "getVastData" as (NSCopying & NSObjectProtocol))

因为我们想异步调用它,所以我们需要存储该引用。我们将使用 JSManagedValue 来存储 JSValue 以避免保留循环。 JSManagedValue 对象包装了一个 JSValue 对象,添加了“条件保留”行为以提供值的自动内存管理。操作完成后,我们可以使用参数调用我们的 javascript 回调函数。简而言之,我们只是将 JS 值从我们的 TVML JS 传递给 Swift,存储它并稍后调用它。

class VastManager: NSObject 

    var completion: JSManagedValue?
    var url: String?

    public func startParsing(from url: String, completion: JSValue) 
        self.completion = JSManagedValue(value: completion)
        self.url = url
    
    ...

    func parserDidEndDocument(_ parser: XMLParser) 

        ...

        _ = self.completion?.value?.call(withArguments: [duration, ads, videoUrl])
    

最后,我们可以从 JavaScript 调用我们的 Swift 函数并传递我们的回调函数。

getVastData(vastTag, function(duration, adLinks, videoLink) 
   ...

// or
function updatePlayer(duration, adLinks, videoLink) 
   ...

getVastData(vastTag, updatePlayer) 
   ...

【讨论】:

evaluate(inJavaScriptContext:) 块中调用完成处理程序的任何具体原因?简单地做completion.call(withArguments:) 似乎在我的应用程序中工作得很好。 @Anders 你的方法更好/更简单,我会更新我的评论。 另一条评论:我不确定您是否应该存储一个非托管的JSValue,因为它可以创建一个保留周期。相反,您最好将completion 属性更改为JSManagedValue。我不完全确定您的设置,但请查看文档并确定它是否与您的用例相关:developer.apple.com/documentation/javascriptcore/jsmanagedvalue @Anders。你是对的,我将完成更改为 JSManagedValue。我对旧解决方案没有任何问题,但这可能会防止将来出现一些错误。

以上是关于从 Java Script (TVML) 调用带有完成处理程序的 Swift 函数的主要内容,如果未能解决你的问题,请参考以下文章

TVML - 浏览模板

TVML 动态添加项目?

在 TVML (tvOS) 中创建分层图像

TVML 菜单栏问题

Java Script函数变量对象

TVML/TVJS 中的动态 XML 模板