TVOS:启动时的竞争条件
TVOS: Race condition at startup
使用模板和 TVML,我用自己的加载页面启动我的应用程序,然后调用服务为用户创建主页。
如果我在 didFinishLaunchingWithOptions
中发起对服务器的调用,我会得到错误 ITML <Error>: undefined is not an object - undefined - line:undefined:undefined
。
由此我假设我对服务器的异步调用在 javascript App.onLaunch
函数完成之前完成,并且只有在服务器之前强制等待一段时间才能让它工作被调用。
这是 AppDelegate 方法:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
let appControllerContext = TVApplicationControllerContext()
// our "base" is local
if let jsBootUrl = NSBundle.mainBundle().URLForResource("application", withExtension: "js") {
appControllerContext.javaScriptApplicationURL = jsBootUrl
}
let jsBasePathURL = appControllerContext.javaScriptApplicationURL.URLByDeletingLastPathComponent
baseUrl = jsBasePathURL?.absoluteString
appControllerContext.launchOptions["BASEURL"] = jsBasePathURL?.absoluteString
appController = TVApplicationController(context: appControllerContext, window: window, delegate: self)
// initiate conversation with the server
myPageCreator = PageCreator()
myPageCreator?.delegate = self
myPageCreator?.startDataCall(baseUrl!)
return true
}
这是(有点样板)javascript 函数:
App.onLaunch = function(options) {
var javascriptFiles = [
`${options.BASEURL}ResourceLoader.js`,
`${options.BASEURL}Presenter.js`
];
evaluateScripts(javascriptFiles, function(success) {
if (success) {
resourceLoader = new ResourceLoader(options.BASEURL);
var index = resourceLoader.loadResource(`${options.BASEURL}myLoadingPage.xml.js`,
function(resource) {
var doc = Presenter.makeDocument(resource);
doc.addEventListener("select", Presenter.load.bind(Presenter));
navigationDocument.pushDocument(doc);
});
} else {
/* handle error case here */
}
});
}
现在,如果我在 didFinishLaunchingWithOptions
中更改对服务器的调用并强制它等待,如下所示:
...
// race condition hack:
_ = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "testing", userInfo: nil, repeats: false)
return true
}
// initiate conversation with the server
func testing() {
myPageCreator = PageCreator()
myPageCreator?.delegate = self
myPageCreator?.startDataCall(baseUrl!)
}
.. 它会起作用的。但我不喜欢那个解决方案!我能做些什么来阻止这种竞争条件的发生?
您需要一种方法让 Javascript 与 Swift 通信,以便您知道 App.onLaunch 何时完成 运行这是脚本。
运行 此代码在您的 didFinishLaunchingWithOptions 方法中。它将允许您在 Javascript 中调用 onLaunchDidFinishLoading() 并在 Swift.
中处理回调
appController.evaluateInJavaScriptContext({(evaluation: JSContext) -> Void in
let onLaunchDidFinishLoading : @convention(block) () -> Void = {
() -> Void in
//when onLaunchDidFinishLoading() is called in Javascript, the code written here will run.
self.testing()
}
evaluation.setObject(unsafeBitCast(onLaunchDidFinishLoading, AnyObject.self), forKeyedSubscript: "onLaunchDidFinishLoading")
}, completion: {(Bool) -> Void in
})
func testing() {
myPageCreator = PageCreator()
myPageCreator?.delegate = self
myPageCreator?.startDataCall(baseUrl!)
}
在 App.onLaunch 中,只需在模板加载完成后添加 onLaunchDidFinishLoading()。
App.onLaunch = function(options) {
var javascriptFiles = [
`${options.BASEURL}ResourceLoader.js`,
`${options.BASEURL}Presenter.js`
];
evaluateScripts(javascriptFiles, function(success) {
if (success) {
resourceLoader = new ResourceLoader(options.BASEURL);
var index = resourceLoader.loadResource(`${options.BASEURL}myLoadingPage.xml.js`,
function(resource) {
var doc = Presenter.makeDocument(resource);
doc.addEventListener("select", Presenter.load.bind(Presenter));
navigationDocument.pushDocument(doc);
//ADD THE FOLLOWING LINE
onLaunchDidFinishLoading();
});
} else {
/* handle error case here */
}
});
}
使用模板和 TVML,我用自己的加载页面启动我的应用程序,然后调用服务为用户创建主页。
如果我在 didFinishLaunchingWithOptions
中发起对服务器的调用,我会得到错误 ITML <Error>: undefined is not an object - undefined - line:undefined:undefined
。
由此我假设我对服务器的异步调用在 javascript App.onLaunch
函数完成之前完成,并且只有在服务器之前强制等待一段时间才能让它工作被调用。
这是 AppDelegate 方法:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
let appControllerContext = TVApplicationControllerContext()
// our "base" is local
if let jsBootUrl = NSBundle.mainBundle().URLForResource("application", withExtension: "js") {
appControllerContext.javaScriptApplicationURL = jsBootUrl
}
let jsBasePathURL = appControllerContext.javaScriptApplicationURL.URLByDeletingLastPathComponent
baseUrl = jsBasePathURL?.absoluteString
appControllerContext.launchOptions["BASEURL"] = jsBasePathURL?.absoluteString
appController = TVApplicationController(context: appControllerContext, window: window, delegate: self)
// initiate conversation with the server
myPageCreator = PageCreator()
myPageCreator?.delegate = self
myPageCreator?.startDataCall(baseUrl!)
return true
}
这是(有点样板)javascript 函数:
App.onLaunch = function(options) {
var javascriptFiles = [
`${options.BASEURL}ResourceLoader.js`,
`${options.BASEURL}Presenter.js`
];
evaluateScripts(javascriptFiles, function(success) {
if (success) {
resourceLoader = new ResourceLoader(options.BASEURL);
var index = resourceLoader.loadResource(`${options.BASEURL}myLoadingPage.xml.js`,
function(resource) {
var doc = Presenter.makeDocument(resource);
doc.addEventListener("select", Presenter.load.bind(Presenter));
navigationDocument.pushDocument(doc);
});
} else {
/* handle error case here */
}
});
}
现在,如果我在 didFinishLaunchingWithOptions
中更改对服务器的调用并强制它等待,如下所示:
...
// race condition hack:
_ = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "testing", userInfo: nil, repeats: false)
return true
}
// initiate conversation with the server
func testing() {
myPageCreator = PageCreator()
myPageCreator?.delegate = self
myPageCreator?.startDataCall(baseUrl!)
}
.. 它会起作用的。但我不喜欢那个解决方案!我能做些什么来阻止这种竞争条件的发生?
您需要一种方法让 Javascript 与 Swift 通信,以便您知道 App.onLaunch 何时完成 运行这是脚本。
运行 此代码在您的 didFinishLaunchingWithOptions 方法中。它将允许您在 Javascript 中调用 onLaunchDidFinishLoading() 并在 Swift.
中处理回调appController.evaluateInJavaScriptContext({(evaluation: JSContext) -> Void in
let onLaunchDidFinishLoading : @convention(block) () -> Void = {
() -> Void in
//when onLaunchDidFinishLoading() is called in Javascript, the code written here will run.
self.testing()
}
evaluation.setObject(unsafeBitCast(onLaunchDidFinishLoading, AnyObject.self), forKeyedSubscript: "onLaunchDidFinishLoading")
}, completion: {(Bool) -> Void in
})
func testing() {
myPageCreator = PageCreator()
myPageCreator?.delegate = self
myPageCreator?.startDataCall(baseUrl!)
}
在 App.onLaunch 中,只需在模板加载完成后添加 onLaunchDidFinishLoading()。
App.onLaunch = function(options) {
var javascriptFiles = [
`${options.BASEURL}ResourceLoader.js`,
`${options.BASEURL}Presenter.js`
];
evaluateScripts(javascriptFiles, function(success) {
if (success) {
resourceLoader = new ResourceLoader(options.BASEURL);
var index = resourceLoader.loadResource(`${options.BASEURL}myLoadingPage.xml.js`,
function(resource) {
var doc = Presenter.makeDocument(resource);
doc.addEventListener("select", Presenter.load.bind(Presenter));
navigationDocument.pushDocument(doc);
//ADD THE FOLLOWING LINE
onLaunchDidFinishLoading();
});
} else {
/* handle error case here */
}
});
}