fuzzlog
fuzzlog

Reputation: 131

Swift: passing a func as a parameter in an initializer

** UPDATED TO REFLECT SUGGESTED CODE AND MORE DETAILED EXPLANATION **

Trying to accomplish the following using the turorial here because I need the subclass instance to delegate functionality to the parent class eventually:

  1. Subclass an SKNode so it can do the following:
    a. Have a property of type SKSpriteNode
    b. Have a property that will accept a callback/event handler func
    c. This property will save a provided callback func upon init
    d. This property will "raise" an event (use the provided callback func which will be a func parent object of this instance)

However, the original code, between '/*... */', gave the following error message:

Cannot find an initializer for type 'ClickableSKSpriteNode' that accepts an argument list of type '(imageName: String, event: (String, string2: String) -> ())'

Regarding this original code, I may not have an initializer that takes a "string", (string, string) ->()", but I do have an initializer that takes "string, Event<(String, String)>" which as far as I understood, it's supposed to take a function of type (String, String) -> () as its stead. (my understanding of how this works most likely is off I presume... I am coming from a c# background)

After making changes suggested by @rikkigibson (non commented code) below, I get a different error (sorry, don't have the code infront of me at this moment).

Anybody have any guidance as to how to change this code to accomplish what I'm trying to do?

The code in question is below:

* EVENT-RELATED CODE *

public class Event<T> {

    public typealias EventHandler = T -> ()

    private var eventHandlers = [Invocable]()

    public func raise(data: T) {
        for handler in self.eventHandlers {
            handler.invoke(data)
        }
    }

    public func addHandler<U: AnyObject>(target: U, handler: (U) -> EventHandler) -> Disposable
    {
            let wrapper = EventHandlerWrapper(target: target, handler: handler, event: self)
            eventHandlers.append(wrapper)
            return wrapper
    }
}


private protocol Invocable: class {
    func invoke(data: Any)
}

private class EventHandlerWrapper<T: AnyObject, U>: Invocable, Disposable {
    weak var target: T?
    let handler: T -> U -> ()
    let event: Event<U>

    init(target: T?, handler: T -> U -> (), event: Event<U>) {
        self.target = target
        self.handler = handler
        self.event = event;
    }

    func invoke(data: Any) -> () {
        if let t = target {
            handler(t)(data as! U)
        }
    }

    func dispose() {
        event.eventHandlers = event.eventHandlers.filter { $0 !== self }
    }
}

public protocol Disposable {
    func dispose()
}


* SUBCLASSED SKNODE-RELATED CODE *
import SpriteKit

class ClickableSKSpriteNode: SKNode
{
    let cBackground: SKSpriteNode
    let event: Event<(String, String)>
    let handler: AnyObject

    /*init(imageName: String, event: Event<(String, String)>)*/
    init(imageName: String, handler: (String, String) -> ())
    {
        self.cBackground = SKSpriteNode(imageNamed: imageName)
        /*self.event = event*/
        self.event = Event<(String, String)>()
        self.handler = self.event.addHander(handler)
        super.init()
    }


    override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
        let touch:UITouch = touches.first as! UITouch
        let positionInScene = touch.locationInNode(self)

        if self.cBackground.containsPoint(positionInScene) {
            self.event.raise("string one", "string two")
        }
    }

    override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {

    }

    override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {

    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}


* SUBCLASS OBJECT INSTANTIATION CODE *

......

let startButton = ClickableSKSpriteNode(imageName: "Start.png", event: self.handleStartClick)

......

func handleStartClick(string1: String, string2: String){
   DebugPring("String1 = \(string1), String2 = \(string2)")
}

Upvotes: 0

Views: 964

Answers (1)

Rikki Gibson
Rikki Gibson

Reputation: 4347

It's a simple type mismatch. Your argument to init is of type (String, String) -> (), not Event<(String, String)>

I suspect an Array<T -> ()> as a member of your node subclass might be sufficient if all you need is multiple subscribers.

Upvotes: 0

Related Questions