How to call a swift function in JS that returns a value? - swift

The following piece of code works like a charm to define a function in Swift (2.0) that I can call from a Javascript resource (tvos). The function storeSetPackageInfo accepts a parameter and returns nothing.
I am trying to understand how I achieve the same goal with a function that accept no parameters and returns a boolean. I don't seem to understand the syntax.
private typealias JavascriptClosure = (JSContext) -> Void
private typealias ObjectivecCompletionBlock = #convention(block) (String) -> Void
func setupStoreSetPackageInfo() {
let selectComponent: JavascriptClosure = {
[unowned self](context: JSContext) -> Void in
let objCompletion: ObjectivecCompletionBlock = {
(str: String) -> Void in
(self.delegate as? myTVAppControllerDelegate)?.storeSetPackageInfo(str)
}
context.setObject(unsafeBitCast(objCompletion, AnyObject.self), forKeyedSubscript: "storeSetPackageInfo")
}
evaluateInJavaScriptContext(selectComponent, completion: nil)
}
I tried multiple approaches which compile but resulting in the JSContext in not finding the function. Any help is very appreciated.

I described one possible way just yesterday in another context: How to retrieve values from settings.bundle in TVML?
AppDelegate.swift
func appController(appController: TVApplicationController, evaluateAppJavaScriptInContext jsContext: JSContext) {
let jsInterface: cJsInterface = cJsInterface();
jsContext.setObject(jsInterface, forKeyedSubscript: "swiftInterface")
}
JsInterface.swift
#objc protocol jsInterfaceProtocol : JSExport {
func getSetting(setting: String) -> String
}
class cJsInterface: NSObject, jsInterfaceProtocol {
func getSetting(setting: String) -> String {
return "<yourSetting>"
}
}
on the JS side...
swiftInterface.getSetting(...)
It's definitely a different syntax compared to your example, but known to work. See https://github.com/iBaa/PlexConnectApp.

After multiple attempts, I found it, the answer and the solution was in front of me at all time... I had tried before but I eventually I had other messy attempts around. To benefit whoever runs into this problems, here is the solution for any signature
private typealias ObjectivecCompletionBlock = #convention(block) () -> Bool
the completion block must match the signature with
() -> Bool in
Therefore the final code is
private typealias JavascriptClosure = (JSContext) -> Void
private typealias ObjectivecCompletionBlock = #convention(block) () -> Bool
func setupStoreSetPackageInfo() {
let selectComponent: JavascriptClosure = {
[unowned self](context: JSContext) -> Void in
let objCompletion: ObjectivecCompletionBlock = {
() -> Bool in
(self.delegate as? myTVAppControllerDelegate)?.storeSetPackageInfo(str)
}
context.setObject(unsafeBitCast(objCompletion, AnyObject.self), forKeyedSubscript: "storeSetPackageInfo")
}
evaluateInJavaScriptContext(selectComponent, completion: nil)
}
Again really straightforward (once you pull the head out of the bucket...)

Related

Implicit cast of generic

I'm working on an event class in Swift. The idea is that you can add a handler in a way that prevents retain cycles. For example:
let myEvent = Event<String>()
myEvent.addHandler(self, MyClass.handleMyEvent)
In a later step I'm adding the += operator etc.
I have the following code:
class Event<Arg> {
private var handlers = [EventHandler<AnyObject, Arg>]()
func addHandler<Target: AnyObject>(_ target: Target, _ method: #escaping (Target) -> (Arg) -> Void ) {
queue.async { [weak self] in
guard let me = self else { return }
let handler = EventHandler(target: target, method: method)
me.handlers.append(handler)
}
}
}
struct EventHandler<Target: AnyObject, Arg> {
weak var target: Target?
var method: (Target) -> (Arg) -> Void
}
The code above doesn't compile. The Swift compiler raises the following error:
Cannot convert value of type 'EventHandler<Target, Arg>' to expected argument type 'EventHandler<AnyObject, _>'
The compiler doesn't seem to understand that a (Target) -> (Arg) -> Void can be implicitly converted to a (AnyObject) -> (Arg) -> Void. I can fix the code by changing the way the handler is created:
let handler = EventHandler(target: target, method: method as! (AnyObject) -> (Arg) -> Void)
But it seems weird that the explicit cast is necessary.
Any ideas? Or suggestions on how I can improve my code?

How can i call this function in another view controller?

I have a model in which i set this function up to check to see if the current user has a Role. I keep throwing errors when i try to call this code in a different view controller function...Im not quite sure on the syntax, its my first time trying to call a function like this. Thanks.
class AdminCheck {
func getAdmin(completionHandler : ((Admin : Bool) -> Void)) {
var roleQuery = PFRole.query()
roleQuery!.whereKey("name", equalTo: "admin")
roleQuery!.getFirstObjectInBackgroundWithBlock() { (roleObject: PFObject?, error) -> Void in
var adminRole = roleObject as! PFRole
adminRole.users.query()!.findObjectsInBackgroundWithBlock({ (users, error) -> Void in
if let objects = users {
if objects == PFUser.currentUser() {
completionHandler(Admin: true)
} else {
completionHandler(Admin: false)
my call is looking like this..
if AdminCheck.getAdmin {(Admin) -> void in
couple errors I'm getting...
Binary operator '==' cannot be applied to operands of type '()' and 'Bool'
Here is an example. It's a good idea to play around with this in an XCode sample project.
class AdminCheck {
func amIAdmin(handler: (Bool) -> Void) {
handler(false);
}
}
var ad = AdminCheck()
ad.amIAdmin { (result:Bool) -> Void in
print(result)
}
Something will like this.
class AdminCheck {
class func getAdmin(completionHandler : ((admin : Bool) -> Void)){
// your handle
}
}
And use it.
AdminCheck.getAdmin { (admin) -> Void in
// your handle
}

Swift Partial application of protocol method is not allowed

I get a strange error message from Swift
Partial application of protocol method is not allowed
I try to pass a function into a class. My code looks like follow
Condition.swift
public protocol Condition {
var type: String { get set }
init(conditionJson: JSON)
func getExpression() -> NSPredicate
func getConditionAction(actionHandler: () -> Void)
}
TimerCondition.swift
public class TimerCondition: Condition {
public var type: String
public var seconds: Int
required public init(conditionJson: JSON) {
self.type = conditionJson["conditionType"].stringValue
self.seconds = conditionJson["conditionType"].intValue
}
public func getExpression() -> NSPredicate {
return NSPredicate(format: "1 == 1")
}
public func getConditionAction(actionHandler: () -> Void){
actionHandler()
}
}
Now in here im getting the error
public class ConditionHandler {
var test:((actionHandler: () -> Void) -> Void)
public init(eventResponse:JSON) {
//error happens here
test = condition.getConditionAction
}
If I do the whole thing without a protocol it works.
Can some help me out here?
EDIT
The solution described in Partial application of protocol method is not allowed can't be applied to my problem, as I want to use a parameter

Is there a better way to do dependency injection in Swift than this ?

New to swift, I was trying to create a service registry:
class ServiceRegistry {
static var instance = ServiceRegistry()
private var registry = [String:AnyObject]()
private init(){}
func register<T>(key:T, value:AnyObject) {
self.registry["\(T.self)"] = value
}
func get<T>(_:T) -> AnyObject? {
return registry["\(T.self)"]
}
}
but is not super friendly:
Register:
ServiceRegistry.instance.register(CacheServiceProtocol.self, value:ImageCacheService())
Retrieve:
if let cache = ServiceRegistry.instance.get(CacheServiceProtocol) as? CacheServiceProtocol { ... }
Any better way ? It would be useful to get rid of the as? CacheServiceProtocol in the if let ...
Swinject is a dependency injection framework for Swift. In your case, you can use it without the cast with as?.
Register:
let container = Container()
container.register(CacheServiceProtocol.self) { _ in ImageCacheService() }
Retrieve:
let cache = container.resolve(CacheServiceProtocol.self)!
Here cache is inferred as CacheServiceProtocol type. The resolve method returns nil if the specified type is not registered. We know CacheServiceProtocol is already registered, so the force-unwrap with ! is used.
UPDATE
I didn't exactly answer to the question. An implementation to remove the cast is storing factory closures instead of values in the registry. Here is the example. I also modified the type of key.
class ServiceRegistry {
static var instance = ServiceRegistry()
private var registry = [String:Any]()
private init(){}
func register<T>(key:T.Type, factory: () -> T) {
self.registry["\(T.self)"] = factory
}
func get<T>(_:T.Type) -> T? {
let factory = registry["\(T.self)"] as? () -> T
return factory.map { $0() }
}
}
Register:
ServiceRegistry.instance.register(CacheServiceProtocol.self) {
return ImageCacheService()
}
Retrieve:
// The type of cache is CacheServiceProtocol? without a cast.
let cache = ServiceRegistry.instance.get(CacheServiceProtocol.self)
Using #autoclosure might be also good.
I see your attempt to implement Service Locator design pattern. It's not Dependency Injection itself, but these two patterns actually can supplement each other.
I did implement a Service Locator in Swift 2 as well and I'm pretty happy with the result. Take a look at my code here: ServiceLocator.swift (ready to use) or BasicServiceLocator.swift and LazyServiceLocator.swift (with usage examples).
Here is the basic concept:
protocol ServiceLocator {
func getService<T>(type: T.Type) -> T?
func getService<T>() -> T?
}
extension ServiceLocator {
func getService<T>() -> T? {
return getService(T)
}
}
func typeName(some: Any) -> String {
return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)"
}
final class BasicServiceLocator: ServiceLocator {
// Service registry
private lazy var reg: Dictionary<String, Any> = [:]
func addService<T>(instance: T) {
let key = typeName(T)
reg[key] = instance
//print("Service added: \(key) / \(typeName(service))")
}
func getService<T>(type: T.Type) -> T? {
return reg[typeName(T)] as? T
}
}
And demonstration:
// Services declaration
protocol S1 {
func f1() -> String
}
protocol S2 {
func f2() -> String
}
// Services imlementation
class S1Impl: S1 {
func f1() -> String {
return "S1 OK"
}
}
class S2Impl: S2 {
func f2() -> String {
return "S2 OK"
}
}
// Service Locator initialization
let sl: ServiceLocator = {
let sl = BasicServiceLocator()
sl.addService(S1Impl() as S1)
sl.addService(S2Impl() as S2)
return sl
}()
// Test run
let s1 = sl.getService(S1)
let s2: S2? = sl.getService(S2)
print(s1?.f1() ?? "S1 NOT FOUND") // S1 OK
print(s2?.f2() ?? "S2 NOT FOUND") // S2 OK
As one of the other posters pointed out, the Service Locator pattern is not actually DI. Some would even go so far as to say it's an anti-pattern.
As a general answer to your question - I do believe first class DI is a better way to accomplish the above. My suggestion would be to use Typhoon but there are several other DI libs available for Swift such as Cleanse which looks very promising.

implicit return in a closure causing an error

Error: Cannot convert the expression type (String, MyType) to ()
From the following code
Test(method: {[weak self] (message: String) in self?.callback(message)}, instance: self)
and if I add a return statement, it works, and the error goes away
Test(method: {[weak self] (message: String) in self?.callback(message); return}, instance: self)
Not sure how to handle the above without having to have the dummy return statement, any advise.
Here's my class Test
public class Test {
private var instance: AnyObject?
private var method: ((message: String) -> ())?
public init(method: (String -> ())?, instance: AnyObject) {
}
}
Edit
I've done a playground based minimalistic example (please copy paste for a test)
class Test {
private var _method: ((String) -> ())?
weak private var _instance: AnyObject?
init(method: (String -> ())?, instance: AnyObject?) {
_method = method
_instance = instance
}
}
class Another {
func register() {
//this doesn't need a return
Test(method: {(message: String) in self.callback(message)}, instance: self)
//this needs a return once I add [weak self]
Test(method: { [weak self] (message: String) in self?.callback(message); return}, instance: self)
}
func callback(message: String) {
println(message)
}
}
Not sure how to handle the above without having to have the dummy return statement, any advise.
You have solved the problem beautifully. Anonymous functions automatically use a one-line function body as a return value, so to prevent that from causing a type mismatch with the expected return type (Void) you have to add another line of code so that it is not a one-line function body. The dummy return statement, which itself returns Void, is a great way to handle it; I would just use that and move on. There are some snazzier workarounds but what you have is precisely what I would do.
EDIT: To understand the source of the type mismatch, try this:
struct Test {
func voider() -> Void {}
}
let testMaybe = Optional(Test())
let result = testMaybe?.voider()
Now result is not a Void; it's an Optional wrapping a Void. That is what's happening to you; a Void is expected but your one-line anonymous function returns an Optional wrapping a Void. By adding another line that returns Void explicitly, you solved the problem.
The implicit return is returning the result of your callback() method. That return value conflicts with the closure's return value of void. You thus need an explicit, if ugly, return.