Como conectar o TVML / JavaScriptCore ao UIKit / Objective-C (Swift)?
Tão longetvOS
suporta duas maneiras de criar aplicativos de TV, TVML e UIKit, e não há menções oficiais sobre como misturar coisas para criar uma interface de usuário TVML (que é basicamente XML) com a contraparte nativa para a lógica do aplicativo e E / S ( como reprodução, streaming, persistência do iCloud etc.).
Então, qual é a melhor solução para misturarTVML
eUIKit
em um novotvOS
aplicativo?
A seguir, tentei uma solução seguindo trechos de código adaptados dos Fóruns da Apple e perguntas relacionadas sobre a ligação JavaScriptCore a ObjC / Swift. Esta é uma classe de wrapper simples no seu projeto Swift.
import UIKit
import TVMLKit
@objc protocol MyJSClass : JSExport {
func getItem(key:String) -> String?
func setItem(key:String, data:String)
}
class MyClass: NSObject, MyJSClass {
func getItem(key: String) -> String? {
return "String value"
}
func setItem(key: String, data: String) {
print("Set key:\(key) value:\(data)")
}
}
onde o delegado deve conformar umTVApplicationControllerDelegate
:
typealias TVApplicationDelegate = AppDelegate
extension TVApplicationDelegate : TVApplicationControllerDelegate {
func appController(appController: TVApplicationController, evaluateAppJavaScriptInContext jsContext: JSContext) {
let myClass: MyClass = MyClass();
jsContext.setObject(myClass, forKeyedSubscript: "objectwrapper");
}
func appController(appController: TVApplicationController, didFailWithError error: NSError) {
let title = "Error Launching Application"
let message = error.localizedDescription
let alertController = UIAlertController(title: title, message: message, preferredStyle:.Alert ) self.appController?.navigationController.presentViewController(alertController, animated: true, completion: { () -> Void in
})
}
func appController(appController: TVApplicationController, didStopWithOptions options: [String : AnyObject]?) {
}
func appController(appController: TVApplicationController, didFinishLaunchingWithOptions options: [String : AnyObject]?) {
}
}
Neste ponto, o javascript é muito simples. Dê uma olhada nos métodos com parâmetros nomeados, você precisará alterar o nome do método da parte do contador javascript:
App.onLaunch = function(options) {
var text = objectwrapper.getItem()
// keep an eye here, the method name it changes when you have named parameters, you need camel case for parameters:
objectwrapper.setItemData("test", "value")
}
App. onExit = function() {
console.log('App finished');
}
Agora, suponha que você tenha uma interface js muito complexa para exportar como
@protocol MXMJSProtocol<JSExport>
- (void)boot:(JSValue *)status network:(JSValue*)network user:(JSValue*)c3;
- (NSString*)getVersion;
@end
@interface MXMJSObject : NSObject<MXMJSProtocol>
@end
@implementation MXMJSObject
- (NSString*)getVersion {
return @"0.0.1";
}
você pode fazer como
JSExportAs(boot,
- (void)boot:(JSValue *)status network:(JSValue*)network user:(JSValue*)c3 );
Neste ponto da parte do contador JS, você não fará o caso camel:
objectwrapper.bootNetworkUser(statusChanged,networkChanged,userChanged)
mas você vai fazer:
objectwrapper.boot(statusChanged,networkChanged,userChanged)
Finalmente, olhe para esta interface novamente:
- (void)boot:(JSValue *)status network:(JSValue*)network user:(JSValue*)c3;
O valor que JSValue * passou. É uma maneira de passar manipuladores de conclusão entreObjC/Swift
eJavaScriptCore
. Neste ponto do código nativo, todos vocês chamam com argumentos:
dispatch_async(dispatch_get_main_queue(), ^{
NSNumber *state = [NSNumber numberWithInteger:status];
[networkChanged.context[@"setTimeout"]
callWithArguments:@[networkChanged, @0, state]];
});
Nas minhas descobertas, vi que o MainThread travará se você não enviar no thread principal e assíncrono. Então, chamarei a chamada javascript "setTimeout" que chama o retorno de chamada do manipulador de conclusão.
Portanto, a abordagem que usei aqui é:
UsarJSExportAs
levar o carro de métodos com parâmetros nomeados e evitar camelo caso homólogos javascript como callMyParam1Param2Param3UsarJSValue
como parâmetro para se livrar dos manipuladores de conclusão. Use callWithArguments no lado nativo. Use funções javascript no lado JS;dispatch_async
para manipuladores de conclusão, possivelmente chamando um setTimeout com 0 atraso no lado do JavaScript, para evitar que a interface do usuário congele.[ATUALIZAR] Atualizei esta questão para ficar mais claro. Estou encontrando uma solução técnica para fazer a ponteTVML
eUIKit
a fim de
JavaScriptCode
Tenha a ponte certa deJavaScriptCore
paraObjectiveC
e vice versaTenha as melhores performances ao ligarJavaScriptCode
deObjective-C