arrays - How do I cast the argument type within a closure signature in Swift? -


i'm trying write light observer class in swift (currently swift 2). idea use within entity component system, means components communicate one-another without being coupled together.

the problem i'm having types of data communicated, cgvector, nstimeinterval , on. means method being passed have kinds of type signatures (cgvector) -> void, () -> void etc.

i'd able store these varying signatures in array, still have type safety. thinking type array (any) -> void or perhaps (any?) -> void, can @ least ensure contains methods. i'm having trouble passing methods in way: cannot convert value of type '(cgvector) -> ()' expected argument type '(any) -> ()'.

first attempt:

//: playground - noun: place people can play  import cocoa import foundation  enum eventname: string {     case input, update }  struct binding{     let listener: component     let action: (any) -> () }  class eventmanager {     var events = [eventname: [binding]]()      func add(name: eventname, event: binding) {         if var eventarray = events[name] {             eventarray.append(event)         } else {             events[name] = [event]         }     }      func dispatch(name: eventname, argument: any) {         if let eventarray = events[name] {             element in eventarray {                 element.action(argument)             }         }     }      func remove(name: eventname, listener: component) {         if var eventarray = events[name] {             eventarray = eventarray.filter(){ $0.listener.doc  != listener.doc }         }     } }  // usage test  //components  protocol component {     var doc: string { } }  class input: component {     let doc = "inputcomponent"     let eventmanager: eventmanager      init(eventmanager: eventmanager) {         self.eventmanager = eventmanager     }      func goright() {         eventmanager.dispatch(.input, argument: cgvector(dx: 10, dy: 0) )     } }  class movement: component {     let doc = "movementcomponent"      func move(vector: cgvector) {         print("moved \(vector)")     }  }  class physics: component {     let doc = "physicscomponent"      func update(time: nstimeinterval){         print("updated @ \(time)")     } }   class someclass {     //events     let eventmanager = eventmanager()      // components     let inputcomponent: input     let movecomponent = movement()      init() {         inputcomponent = input(eventmanager: eventmanager)          let inputbinding = binding(listener: movecomponent, action: movecomponent.move) // ! cannot convert value of type '(cgvector) -> ()' expected argument type '(any) -> ()'         eventmanager.add(.input, event: inputbinding)      } }  let someinstance = someclass() someinstance.inputcomponent.goright() 

throws error cannot convert value of type '(cgvector) -> ()' expected argument type '(any) -> ()'.

second attempt

if genericize binding struct recognise different types of arguments have bit more luck. version works, array holding methods [any] ( i'm not sure whether it's attempt cast any binding struct causing odd error below binary operator '!=' cannot applied 2 'string' operands):

struct binding<argument>{     let listener: component     let action: (argument) -> () }  class eventmanager {     var events = [eventname: [any]]()      func add(name: eventname, event: any) {         if var eventarray = events[name] {             eventarray.append(event)         } else {             events[name] = [event]         }     }      func dispatch<argument>(name: eventname, argument: argument) {         if let eventarray = events[name] {             element in eventarray {                 (element as! binding<argument>).action(argument)             }         }     }      func remove(name: eventname, listener: component) {         if var eventarray = events[name] {            // eventarray = eventarray.filter(){ ($0 as! binding).listener.doc  != listener.doc } //binary operator '!=' cannot applied 2 'string' operands         }     } } 

is there way , have array hold methods of varying type signatures, [(any?) -> ()] ?

attempt 3...

reading around, eg here http://www.klundberg.com/blog/capturing-objects-weakly-in-instance-method-references-in-swift/ seems approach above lead strong reference cycles, , need pass static method eg movement.move rather movecomponent.move. type signature storing (component) -> (any?) -> void rather (any?) -> void. question still stands, still able store array of these static methods bit more type-safety [any].

one approach casting parameters of closure, suggested in mike ash's blog casey fleser linked to, "recurry"(?) it.

a genericised binding class:

private class binding<argument>{     weak var listener: anyobject?     let action: anyobject -> argument -> ()      init(listener: anyobject, action: anyobject -> argument -> ()) {         self.listener = listener         self.action = action     }      func invoke(data: argument) -> () {         if let = listener {             action(this)(data)         }     } } 

and event manager, without recurrying:

class eventmanager {      var events = [eventname: [anyobject]]()      func add<t: anyobject, argument>(name: eventname, listener: t, action: t -> argument -> void) {                    let binding = binding(listener: listener, action: action) //error: cannot convert value of type 't -> argument -> void' expected argument type 'anyobject -> _ -> ()'          if var eventarray = events[name] {             eventarray.append(binding)         } else {             events[name] = [binding]         }     }      func dispatch<argument>(name: eventname, argument: argument) {         if let eventarray = events[name] {             element in eventarray {                 (element as! binding<argument>).invoke(argument)             }         }     }      func remove(name: eventname, listener: component) {         if var eventarray = events[name] {             eventarray = eventarray.filter(){ $0 !== listener }         }     } } 

this still produces same error, of not being able cast anyobject:

error: cannot convert value of type 't -> argument -> void' expected argument type 'anyobject -> _ -> ()'.

bu if call first part of curried function, , enclose within new closure (i don't know if has name, i'm calling "recurrying"), this: action: { action($0 as! t) } works (technique taken mike ash). guess bit of hack, in swift type safety being circumvented.

i don't understand error message: it's saying can't convert t anyobject, accepts casting t?

edit: updated complete code far edit2: corrected how events appended edit3: removing events works

//: playground - noun: place people can play  import cocoa import foundation  enum eventname: string {     case input, update }  private class binding<argument>{     weak var listener: anyobject?     let action: anyobject -> argument -> ()      init(listener: anyobject, action: anyobject -> argument -> ()) {         self.listener = listener         self.action = action     }      func invoke(data: argument) -> () {         if let = listener {             action(this)(data)         }     }  }   class eventmanager {     var events = [eventname: [anyobject]]()      func add<t: anyobject, argument>(name: eventname, listener: t, action: t -> argument -> void) {          let binding = binding(listener: listener, action: { action($0 as! t)  }) //          if events[name]?.append(binding) == nil {             events[name] = [binding]         }     }      func dispatch<argument>(name: eventname, argument: argument) {         if let eventarray = events[name] {             element in eventarray {                 (element as! binding<argument>).invoke(argument)             }         }     }      func remove<t: anyobject, argument>(name: eventname, listener: t, action: t -> argument -> void) {         events[name]? = events[name]!.filter(){ ( $0 as! binding<argument>).listener !== listener }     } }  // usage test  //components  class component {      weak var events: eventmanager?     let doc: string     init(doc: string){         self.doc = doc     }  }   class input: component {      init() {         super.init(doc: "inputcomponent")     }      func goright() {         events?.dispatch(.input, argument: cgvector(dx: 10, dy: 0) )     }      func goup() {         events?.dispatch(.input, argument: cgvector(dx: 0, dy: -5) )     } }  class movement: component {     init() {         super.init(doc: "movementcomponent")     }     func move(vector: cgvector) {         print("moved \(vector)")     }  }   class physics: component {     init() {         super.init(doc: "physicscomponent")     }      func update(time: nstimeinterval){         print("updated @ \(time)")     }      func move(vector: cgvector) {         print("updated \(vector)")     }  }  // entity  class entity {      let events = eventmanager()  }   class someclass: entity {      // components     let inputcomponent: input     let movecomponent: movement     let physicscomponent: physics      override init() {          inputcomponent = input()         movecomponent = movement()         physicscomponent = physics()         super.init()         inputcomponent.events = events          events.add(.input, listener: movecomponent, action: movement.move)         events.add(.input, listener: physicscomponent, action: physics.move)     } }  let someinstance = someclass()  someinstance.inputcomponent.goright() //moved cgvector(dx: 10.0, dy: 0.0) //updated cgvector(dx: 10.0, dy: 0.0)  someinstance.events.remove(.input, listener: someinstance.movecomponent, action: movement.move) someinstance.inputcomponent.goup() //updated cgvector(dx: 0.0, dy: -5.0)  someinstance.events.remove(.input, listener: someinstance.physicscomponent, action: physics.move) someinstance.inputcomponent.goright() // nothing 

Comments

Popular posts from this blog

sql - invalid in the select list because it is not contained in either an aggregate function -

Angularjs unit testing - ng-disabled not working when adding text to textarea -

How to start daemon on android by adb -