Gorilla session extracting structure value from session golang - google-app-engine

I created a user entry in my gorilla session
session.Values["ub"] = ub
However when I am extracting, the extracted interface is getting the value but when I am type-asserting the data into my user structure it is showing null.
I am not getting the reason behind this, as when I am running the same code with other structures, it is working fine. What could be the reason and how to fix this.
val := session.Values["ub"]
var ub = &UserBasic{}
var ok bool
if val != "" {
//type assertion
//var userBasic = &auth.UserBasic{}
ub, ok = val.(*UserBasic)
if !ok {
// Handle the case that it's not an expected type
log.Infof(ctx, "GetUserGSession: UserBasic structure value %+v", ub)
log.Infof(ctx, "GetUserGSession: Value got from session %+v", val)
log.Infof(ctx, "GetUserGSession: reasserting %+v", val.(UserBasic))
}
return ub
} else {
ub := new(UserBasic)
return ub
}
*********Outcome************
2016/08/01 03:49:12 INFO: GetUserGSession: UserBasic structure value <nil>
2016/08/01 03:49:12 INFO: GetUserGSession: Value got from session {UserID:589578337 UserName:ds Name:ds createdAt:{sec:0 nsec:0 loc:<nil>} defaultProfileImage:false description: FavouritesCount:5 FollowersCount:0 FriendsCount:0 TotalTweets:6 ListedCount:0 Location: ProfileBgColor: ProfileBgImgURL: ProfileImgURL: IsLoggedIn:true TimeZone:}
2016/08/01 03:49:12 INFO: GetUserGSession: reasserting {UserID:589578337 UserName:ds Name:ds createdAt:{sec:0 nsec:0 loc:<nil>} defaultProfileImage:false description: FavouritesCount:5 FollowersCount:0 FriendsCount:0 TotalTweets:6 ListedCount:0 Location: ProfileBgColor: ProfileBgImgURL: ProfileImgURL: IsLoggedIn:true TimeZone:}

If your interface method has pointer receiver you should create it as a pointer, like this:
var val Worker = &UserBasic{100}
and if you use var val Worker = UserBasic{100} you will see this error:
UserBasic does not implement Worker (Work method has pointer receiver)
in this working sample code:
package main
import "fmt"
var _ Worker = (*UserBasic)(nil) // Verify that *UserBasic implements Worker.
func main() {
var val Worker = &UserBasic{100}
ub, ok := val.(*UserBasic)
fmt.Println(ub, ok) // &{100} true
}
type Worker interface {
Work()
}
type UserBasic struct {
A int
}
func (t *UserBasic) Work() {
fmt.Println("Work.")
}
you may check at compile-time if a type implements an interface or not:
Verify that *UserBasic implements Worker (Compile: Success):
package main
import "fmt"
var _ Worker = (*UserBasic)(nil) // Verify that *UserBasic implements Worker.
func main() {
fmt.Println("Hello World!")
}
type Worker interface {
Work()
}
type UserBasic struct{}
func (t *UserBasic) Work() {
fmt.Println("Work.")
}
Verify that UserBasic implements Worker (Compile: Success):
package main
import "fmt"
var _ Worker = UserBasic{} // Verify that UserBasic implements Worker.
func main() {
fmt.Println("Hello World!")
}
type Worker interface {
Work()
}
type UserBasic struct{}
func (t UserBasic) Work() {
fmt.Println("Work.")
}
for test sample compile this code (Compile: error):
package main
import "fmt"
var _ Worker = (*UserBasic)(nil) // Verify that *UserBasic implements Worker.
func main() {
fmt.Println("Hello World!")
}
type Worker interface {
Work()
}
type UserBasic struct{}
it says:
.\m.go:5: cannot use (*UserBasic)(nil) (type *UserBasic) as type Worker in assignment:
*UserBasic does not implement Worker (missing Work method)

Related

sqlc + pgx: array of user defined enum: cannot scan unknown type (OID 16385) in text format

I'm using sqlc and pgx/v5, and getting the below error for a postgres array of a user defined enum type:
Error: can't scan into dest[1]: cannot scan unknown type (OID 16385) in text format into *pgtype.Array[my-app/sqlc.Option]
schema and query:
CREATE TYPE option AS ENUM (
'OPT_1',
'OPT_2',
'OPT_3'
);
CREATE TABLE IF NOT EXISTS blah (
id BIGINT PRIMARY KEY,
options option[] NOT NULL DEFAULT '{OPT_1}'
);
-- name: CreateBlah :one
INSERT INTO blah (
id
) VALUES (
$1
)
RETURNING *;
sqlc appears to generate the types correctly:
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.16.0
package sqlc
import (
"database/sql/driver"
"fmt"
"github.com/jackc/pgx/v5/pgtype"
)
type Option string
const (
OptionOPT1 Option = "OPT_1"
OptionOPT2 Option = "OPT_2"
OptionOPT3 Option = "OPT_3"
)
func (e *Option) Scan(src interface{}) error {
switch s := src.(type) {
case []byte:
*e = Option(s)
case string:
*e = Option(s)
default:
return fmt.Errorf("unsupported scan type for Option: %T", src)
}
return nil
}
type NullOption struct {
Option Option
Valid bool // Valid is true if Option is not NULL
}
// Scan implements the Scanner interface.
func (ns *NullOption) Scan(value interface{}) error {
if value == nil {
ns.Option, ns.Valid = "", false
return nil
}
ns.Valid = true
return ns.Option.Scan(value)
}
// Value implements the driver Valuer interface.
func (ns NullOption) Value() (driver.Value, error) {
if !ns.Valid {
return nil, nil
}
return string(ns.Option), nil
}
func (e Option) Valid() bool {
switch e {
case OptionOPT1,
OptionOPT2,
OptionOPT3:
return true
}
return false
}
type Blah struct {
ID int64
Options pgtype.Array[Option]
}
I can work around it by defining my own type and implementing the scanner interface then specifying an override in the sqlc configuration:
package types
import (
"fmt"
"strings"
"github.com/jackc/pgx/v5/pgtype"
)
type Options pgtype.Array[string] // <-- cannot be pgtype.Array[sqlc.Option], causes import cycle
func (opts *Options) Scan(src any) error {
opts, ok := src.(string)
if !ok {
return fmt.Errorf("unsupported scan type for Options: %T", src)
}
options := strings.Split(strings.Trim(opts, "{}"), ",")
*opts = Options(pgtype.Array[string]{Elements: options, Valid: true})
return nil
}
// sqlc.yaml
...
overrides:
- column: "blah.options"
go_type: "myapp/pgx/types.Options" // <-- cannot be "sqlc.Options"
But the underlying type must be pgtype.Array[string], it cannot be pgtype.Array[Option], because:
sqlc cannot override a type from within the same package as the generated code
I cannot import the sqlc generated Option type in the defined Options type, as it causes an import cycle (pkg types importing sqlc.Option and pkg sqlc importing types.Options)
This means I lose type safety and the additional methods of the Option type generated by sqlc.
From this pgx/v5 github issue, I think I need to use the pgx/v5 SQLScanner type and calling it's RegisterDefaultPgType method, but, I'm not sure if that's accurate, or how to actually do that.
What is the correct way to have pgx recognize a postgres array of user defined enum type, without losing type safety?

Holding and releasing Closures in an array

I'm looking to make an observable property without relying on a reactive 3rd party lib / framework.
I read this and came up with a similar solution to their Observable Properties answer...
https://blog.scottlogic.com/2015/02/11/swift-kvo-alternatives.html
Theirs
class Observable<T> {
let didChange = Event<(T, T)>()
private var value: T
init(_ initialValue: T) {
value = initialValue
}
func set(newValue: T) {
let oldValue = value
value = newValue
didChange.raise(oldValue, newValue)
}
func get() -> T {
return value
}
}
Mine
public class Observable<V> {
public var value: V { didSet { for observer in observers { observer(value) } }}
private var observers = [(V) -> Void]()
public init(_ initital: V) {
value = initital
}
public func observe(with closure: #escaping (V) -> Void) {
observers.append(closure)
}
}
the only difference is I want to capture an array of closures instead of using Event and addHander... the reason being I want to provide the syntax of passing the value through rather than have the consumers of my code make a function every time and again to not rely on any 3rd party code.
I'm not sure how these closures could automatically be removed from the array once their owners are deallocated. I'm guessing they can't which is why addHandler is used, I'm just hoping someone out there more knowledgable than me can shed some light on the issue.
Thanks for your time.
So I come up with this solution:
class Wrapper<V> {
var observer: (V) -> Void
public init(_ b: #escaping (V) -> Void) {
observer = b
}
}
class Observable<V> {
public var value: V { didSet {
let enumerator = observers.objectEnumerator()
while let wrapper = enumerator?.nextObject() {
(wrapper as! Wrapper<V>).observer(value)
}
}}
private var observers = NSMapTable<AnyObject, Wrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.strongMemory])
public init(_ initital: V) {
value = initital
}
public func observe(_ subscriber: AnyObject, with closure: #escaping (V) -> Void) {
let wrapper = Wrapper(closure)
observers.setObject(wrapper, forKey: subscriber)
}
}
The final API require subscriber to identify themselves at calling:
Observable.observe(self /* <-- extra param */) { /* closure */ }
Although we cannot weak ref a closure, but with NSMapTable, we can weak ref the subscriber object, then use it as a weak key to track observer closure. This allow deallocation of subscriber thus automatically cleanup outdated observers.
Finally, here's the code for a demo. Expand the snippet and copy-paste to swift playground and see it live.
import Foundation
func setTimeout(_ delay: TimeInterval, block:#escaping ()->Void) -> Timer {
return Timer.scheduledTimer(timeInterval: delay, target: BlockOperation(block: block), selector: #selector(Operation.main), userInfo: nil, repeats: false)
}
class Wrapper<V> {
var observer: (V) -> Void
public init(_ b: #escaping (V) -> Void) {
observer = b
}
}
class Observable<V> {
public var value: V { didSet {
let enumerator = observers.objectEnumerator()
while let wrapper = enumerator?.nextObject() {
(wrapper as! Wrapper<V>).observer(value)
}
}}
private var observers = NSMapTable<AnyObject, Wrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.strongMemory])
public init(_ initital: V) {
value = initital
}
public func observe(_ subscriber: AnyObject, with closure: #escaping (V) -> Void) {
let wrapper = Wrapper(closure)
observers.setObject(wrapper, forKey: subscriber)
}
}
class Consumer {
private var id: String
public init(_ id: String, _ observable: Observable<Int>) {
self.id = id
observable.observe(self) { val in
print("[\(id)]", "ok, i see value changed to", val)
}
}
deinit {
print("[\(id)]", "I'm out")
}
}
func demo() -> Any {
let observable = Observable(1)
var list = [AnyObject]()
list.append(Consumer("Alice", observable))
list.append(Consumer("Bob", observable))
observable.value += 1
// pop Bob, so he goes deinit
list.popLast()
// deferred
setTimeout(1.0) {
observable.value += 1
observable.value += 1
}
return [observable, list]
}
// need to hold ref to see the effect
let refHolder = demo()
Edit:
As OP #Magoo commented below, the Wrapper object is not properly deallocated. Even though the subscriber object is successfully deallocated, and corresponding key is removed from NSMapTable, the Wrapper remain active as an entry held in NSMapTable.
Did a bit of test and found this is indeed the case, unexpectedly. Some further research reveal an unfortunate fact: it's a caveat in NSMapTable's implementation.
This post explain the reason behind thoroughly. Quote directly from Apple doc:
However, weak-to-strong NSMapTables are not currently recommended, as the strong values for weak keys which get zero’d out do not get cleared away (and released) until/unless the map table resizes itself.
Hmm, so basically Apple just think it's ok to keep them alive in memory until a resize takes place. Reasonable from GC strategy POV.
Conclusion: no chance it'd be handled if NSMapTables implementation remains the same.
However it shouldn't be a problem for most case. This Observer impl works as intended. And as long as the Wrapper don't do anything fishy and closure don't hold strong ref, only negative impact is just some extra memory footprint.
I do have a fix though, you can use weak -> weak map, so Wrapper as a weak value get dealloc too. But that'll require .observe() returns the Wrapper then Consumer get hold to a ref to it. I'm not keen on this idea, API not ergonomic to end user. I'd rather live with some memory overhead in favor of better API.
Edit 2:
I don't like the aforementioned fix cus the resulting API is not friendly. I saw no way around but #Magoo managed to NAIL IT! Using objc_setAssociatedObject API, which I never heard about before. Make sure to checkout his answer for detail, it's awesome.
OK so #hackape answer with objc_setAssociatedObject
public class Observable<V> {
private class ClosureWrapper<V> {
var closure: (V) -> Void
public init(_ closure: #escaping (V) -> Void) {
self.closure = closure
}
}
private var observers = NSMapTable<AnyObject, ClosureWrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.weakMemory])
public var value: V { didSet { notify() } }
public init(_ initital: V) {
value = initital
}
public func addObserver(_ object: AnyObject, skipFirst: Bool = true, closure: #escaping (V) -> Void) {
let wrapper = ClosureWrapper(closure)
let reference = "observer\(UUID().uuidString)".replacingOccurrences(of: "-", with: "")
observers.setObject(wrapper, forKey: object)
// Giving the closure back to the object that is observing
// allows ClosureWrapper to die at the same time as observing object
objc_setAssociatedObject(object, reference, wrapper, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
if !skipFirst { closure(value) }
}
private func notify() {
let enumerator = observers.objectEnumerator()
while let wrapper = enumerator?.nextObject() { (wrapper as? ClosureWrapper<V>)?.closure(value) }
}
}
This guy also remade the NSMapTable in Swift using a really similar method https://codereview.stackexchange.com/questions/85709/generic-nsmaptable-replacement-written-in-swift
The easiest, and likely safest, solution would be use the exact implementation you have, but make sure all callers use [weak self] and guard that self still exists before performing any actions/side effects.
This way when the array of closures is executed, any that had their creator already dealloc will simply immediately return when called.
// called from outside class
observer.observe { [weak self] in
guard strongSelf = self else { return }
// do work using `strongSelf`
}
If the observer is going to be used by a lot of instances that are constantly deallocating, I'd recommend adding a remove observer function. To do this you'd probably want to return a string on the observe call and then use it for removing the closure. Something along the lines of this:
public typealias ObserverIdentifier = String
public class Observable<V> {
public var value: V { didSet { for observer in observers.values { observer(value) } }}
private var observers = [ObserverIdentifier : (V) -> Void]()
public init(_ initital: V) {
value = initital
}
#discardableResult public func observe(with closure: #escaping (V) -> Void) -> ObserverIdentifier {
let identifier = UUID().uuidString
observers[identifier] = closure
return identifier
}
public func remove(identifier: ObserverIdentifier) {
observers.removeValue(forKey: identifier)
}
}
Because you are using [weak self], removing the observer on deallocation is simply a nice thing to do to avoid some additional no-ops, but still completely safe if not removed.

Get Object by specific field

I'm actually learning go, following some tutorial as this one to build an Resftul API app.
First time Using Go and mongoDB, I don't understand well, how to get specific key in certain document of my collection.
Actually I have this object model :
type Numobject struct {
ID bson.ObjectId `bson:"_id" json:"id"`
Text string `bson:"text" json:"text"`
Number int `bson:"number" json:"number"`
Found bool `bson:"found" json:"found"`
Type string `bson:"type" json:"type"`
}
And I can have a specific object by ID with this function :
// Find Object by ID
func (m *NumObjectDAO) FindByNumber(id string) (Numobject, error) {
var numObject Numobject
err := db.C(COLLECTION).FindId(bson.ObjectIdHex(id)).One(&numObject)
return numObject, err
}
I call my method in main.go as follow
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/gorilla/mux"
"gopkg.in/mgo.v2/bson"
. "github.com/sandaleRaclette/03-coddingChallenge/config"
. "github.com/sandaleRaclette/03-coddingChallenge/dao"
. "github.com/sandaleRaclette/03-coddingChallenge/models"
)
// Save the configuration of mongodatabase (localhost and which db use) in Config array
var config = Config{}
var dao = NumObjectDAO{}
// GET list of all objects
func AllObjectsEndPoint(w http.ResponseWriter, r *http.Request) {
movies, err := dao.FindAll()
if err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
return
}
respondWithJson(w, http.StatusOK, movies)
}
// GET an Object by its ID
func FindObjectEndpoint(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
numObject, err := dao.FindByNumber(params["number"])
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid Object ID")
return
}
respondWithJson(w, http.StatusOK, numObject)
}
// Respond Methods
func respondWithError(w http.ResponseWriter, code int, msg string) {
respondWithJson(w, code, map[string]string{"error": msg})
}
func respondWithJson(w http.ResponseWriter, code int, payload interface{}) {
response, _ := json.Marshal(payload)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
w.Write(response)
}
// Parse the configuration file 'config.toml', and establish a connection to DB
func init() {
config.Read()
dao.Server = config.Server
dao.Database = config.Database
dao.Connect()
}
// Define HTTP request routes and define the differents endpoints
func main() {
r := mux.NewRouter()
r.HandleFunc("/api/v1/trivia", AllObjectsEndPoint).Methods("GET")
r.HandleFunc("/api/v1/trivia/{number}", FindObjectEndpoint).Methods("GET")
if err := http.ListenAndServe(":3000", r); err != nil {
log.Fatal(err)
}
}
There is other methods than getting an object by ID? How can I get an object with a specific key as Number or Type following my model ?
I want to obtain something like this when I GET "/api/v1/trivia/45000000" :
{
"id": "5aa554c89d63b0d3580449a5",
"text": "45000000 is the number of turkeys Americans eat at Thanksgiving annually.",
"number": 45000000,
"found": true,
"type": "trivia"
}
I'm actually looking here for some answers but I have some difficulties with query... If someone can give me beginner explanation...
Use Find for general queries:
err := db.c(COLLECTION).Find(bson.D{{"Number", 10}}).One(&numobjecct)

Memory management of Swift closures passed to C functions

A common idiom in C for callback functions is:
void set_callback(
/* the object using the callback */
void *obj,
/* a pointer to user data */
void *context,
/* the callback function, first parameter is context above */
int (*callback_func)(void *context, obj *a, obj *b),
/* an optional destructor function for cleaning up context */
void (*destructor_function)(void *)
);
destructor_function is called when obj is destroyed to clean up any memory associated with context.
I'm trying to figure out the best way to utilize this code structure in Swift. Specifically, I'm writing an SQLite wrapper and I'd like to be able to add new collation sequences with an API like:
typealias StringComparator = (String, String) -> ComparisonResult
func add(collation name: String, _ block: #escaping StringComparator)
In Obj-C I would Block_copy(block) as context, use the copied block in the callback itself, and Block_release(context) in the destructor function.
Thus far I haven't found a solution I like equally well in Swift, but I have come up with two working alternatives. I've omitted most error handling in the code for brevity.
Option One
// Assume `db` is an `sqlite3 *` object from somewhere
func add(collation name: String, _ block: #escaping StringComparator) {
let storage = UnsafeMutablePointer<StringComparator>.allocate(capacity: 1)
storage.initialize(to: block)
sqlite3_create_collation_v2(db, name, SQLITE_UTF8, storage, { (context, lhs_len, lhs_data, rhs_len, rhs_data) -> Int32 in
guard let lhs = String(bytesNoCopy: UnsafeMutableRawPointer(mutating: lhs_data.unsafelyUnwrapped), length: Int(lhs_len), encoding: .utf8, freeWhenDone: false) else { return 0 }
guard let rhs = String(bytesNoCopy: UnsafeMutableRawPointer(mutating: rhs_data.unsafelyUnwrapped), length: Int(rhs_len), encoding: .utf8, freeWhenDone: false) else { return 0 }
let storage = context.unsafelyUnwrapped.assumingMemoryBound(to: StringComparator.self)
return Int32(storage.pointee(lhs, rhs).rawValue)
}, { context in
let storage = context.unsafelyUnwrapped.assumingMemoryBound(to: StringComparator.self)
storage.deinitialize()
storage.deallocate(capacity: 1)
})
}
While this works in my limited testing, I am not sure whether block is actually copied when assigned to storage. I have a feeling this is a fragile solution.
Option Two
class CollationSequence {
var comparator: StringComparator
init(_ comparator: #escaping StringComparator) {
self.comparator = comparator
}
}
func add(collation name: String, _ block: #escaping StringComparator) {
let collationSequence = CollationSequence(block)
let storage = Unmanaged.passRetained(collationSequence)
sqlite3_create_collation_v2(db, name, SQLITE_UTF8, storage.toOpaque(), { (context, lhs_len, lhs_data, rhs_len, rhs_data) -> Int32 in
guard let lhs = String(bytesNoCopy: UnsafeMutableRawPointer(mutating: lhs_data.unsafelyUnwrapped), length: Int(lhs_len), encoding: .utf8, freeWhenDone: false) else { return 0 }
guard let rhs = String(bytesNoCopy: UnsafeMutableRawPointer(mutating: rhs_data.unsafelyUnwrapped), length: Int(rhs_len), encoding: .utf8, freeWhenDone: false) else { return 0 }
let collationSequence = Unmanaged<CollationSequence>.fromOpaque(context.unsafelyUnwrapped).takeUnretainedValue()
return Int32(collationSequence.comparator(lhs, rhs).rawValue)
}, { context in
_ = Unmanaged<CollationSequence>.fromOpaque(context.unsafelyUnwrapped).takeRetainedValue()
})
}
I feel more confident about the memory management in this solution, but I dislike it because of the introduction of a class that doesn't serve as much more than a coat hanger.
Which of these solutions is preferable? Is there a more elegant solution I've missed?

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

I'm trying to write a light observer class in Swift (currently Swift 2). The idea is to use it within an Entity Component system, as a means for the components to communicate with one-another without being coupled together.
The problem I'm having is that all types of data could be communicated, a CGVector, an NSTimeInterval and so on. This means that the method being passed could have all kinds of type signatures (CGVector) -> Void, () -> Void etc.
I'd like to be able to store these varying signatures in an array, but still have some type safety. My thinking is that the type for the array would be (Any) -> Void or perhaps (Any?) -> Void, so that I can at least ensure that it contains methods. But I'm having trouble passing methods in this way: Cannot convert value of type '(CGVector) -> ()' to expected argument type '(Any) -> ()'.
First attempt:
//: Playground - noun: a place where 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] {
for 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 { get }
}
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 at \(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) -> ()' to expected argument type '(Any) -> ()'
eventManager.add(.input, event: inputBinding)
}
}
let someInstance = someClass()
someInstance.inputComponent.goRight()
Throws the error Cannot convert value of type '(CGVector) -> ()' to expected argument type '(Any) -> ()'.
Second attempt
If I genericize the Binding struct to recognise different types of arguments I have a bit more luck. This version basically works, but the array holding the methods is now [Any] ( I'm not sure whether it's the attempt to cast Any back to the Binding struct that is causing the slightly odd error below Binary operator '!=' cannot be applied to two '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] {
for 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 be applied to two 'String' operands
}
}
}
Is there a way to do this and have the array hold methods of varying type signatures, something like [(Any?) -> ()] ?
Attempt 3...
Reading around, eg here http://www.klundberg.com/blog/capturing-objects-weakly-in-instance-method-references-in-swift/ it seems that my approach above will lead to strong reference cycles, and that what I need to do is pass the static method eg Movement.move rather than moveComponent.move. So the type signature I would be storing would actually be (Component) -> (Any?) -> Void rather than (Any?) -> Void. But my question still stands, I still would like to be able to store an array of these static methods with a bit more type-safety than just [Any].
One approach to casting the parameters of a closure, suggested in Mike Ash's blog that Casey Fleser linked to, is 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 this = listener {
action(this)(data)
}
}
}
And the event manager, without the 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' to 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] {
for 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 the same error, of not being able to cast to AnyObject:
error: cannot convert value of type 'T -> Argument -> Void' to expected argument type 'AnyObject -> _ -> ()'.
Bu if we call the first part of the curried function, and enclose it within a new closure (I don't know if this has a name, I'm calling it "recurrying"), like this: action: { action($0 as! T) } then it all works (technique taken from Mike Ash). I guess this is a bit of a hack, in that Swift type safety is being circumvented.
I also don't really understand the error message: it's saying it can't convert T to AnyObject, but then accepts casting to T?
EDIT: updated with the complete code so far
edit2: corrected how events are appended
edit3: removing events now works
//: Playground - noun: a place where 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 this = 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] {
for 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 at \(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
A different approach to how to store a collection of different type signatures. Instead of using generics, casting, or type erasure, use an enum with associated types representing each type of signature you want to use eg
enum Signature{
case cgvector(CGVector -> Void)
case nstimeinterval(NSTimeInterval -> Void)
The disadvantage is that the enum captures a strong reference to the method. However (I need to take this out of a playground to test it more), this doesn't seem to create a strong reference cycle. You can set the containing entity to nil and all of its components appear to be deinitialized. I'm not quite sure what's happening there. To me this enum approach seems cleaner than putting a generic wrapper in an array of AnyObject and have to cast and type-erase constantly.
Comments and criticisms welcome.
/*:
## Entity - Component framework with a notification system for decoupled communications between components
### Limitations:
1. Closure class stores a strong reference to the components. But, a strong reference cycle is not created.
2. A given class instance (component) can only subscribe to a given event with one method.
*/
import Cocoa
import Foundation
enum EventName: String {
case onInput
}
//A type-safe wrapper that stores closures of varying signatures, and allows them to be identified by the hashValue of its owner.
class Closure {
enum Signature {
case cgvector(CGVector -> Void)
case nstimeinterval(NSTimeInterval -> Void)
func invoke(argument: Any){
switch self {
case let .cgvector(closure): closure(argument as! CGVector)
case let .nstimeinterval(closure): closure(argument as! NSTimeInterval)
}
}
}
var method: Signature
weak var owner: Component?
init(owner: Component, action: Closure.Signature) {
method = action
self.owner = owner
}
}
// Entity
class Entity {
var components = Set<Component>()
private var events = [EventName: [Closure]]()
deinit {
print("Entity deinit")
}
// MARK: component methods
func add(component: Component){
components.insert(component)
component.parent = self
}
func remove(component: Component){
unsubscribeFromAll(component)
components.remove(component)
}
func remove<T: Component>(type: T.Type){
guard let component = retrieve(type) else {return}
remove(component)
}
func retrieve<T: Component>(type: T.Type) -> T? {
for item in components {
if item is T { return item as? T}
}
return nil
}
// MARK: event methods
func subscribe(listener: Component, method: Closure.Signature, to event: EventName ){
let closure = Closure(owner: listener, action: method)
// if event array does not yet exist, create it with the closure.
if events[event] == nil {
events[event] = [closure]
return
}
// check to make sure this listener has not subscribed to this event already
if ((events[event]!.contains({ $0.owner! == listener })) == false) {
events[event]!.append(closure)
}
}
func dispatch(argument: Any, to event: EventName ) {
events[event]?.forEach(){ $0.method.invoke(argument) }
}
func unsubscribe(listener: Component, from name: EventName){
//events[name]? = events[name]!.filter(){ $0.hashValue != listener.hashValue }
if let index = events[name]?.indexOf({ $0.owner! == listener }) {
events[name]!.removeAtIndex(index)
}
}
func unsubscribeFromAll(listener: Component){
for (event, _) in events {
unsubscribe(listener, from: event)
}
}
}
//Components
class Component: Hashable {
weak var parent: Entity?
var doc: String { return "Component" }
var hashValue: Int { return unsafeAddressOf(self).hashValue }
deinit {
print("deinit \(doc)")
}
}
func == <T: Component>(lhs: T, rhs: T) -> Bool {
return lhs.hashValue == rhs.hashValue
}
//: #### Usage test
class Input: Component {
override var doc: String { return "Input" }
func goRight() {
parent?.dispatch(CGVector(dx: 10, dy: 0), to: .onInput )
}
func goUp() {
parent?.dispatch(CGVector(dx: 0, dy: -10), to: .onInput )
}
}
class Movement: Component {
override var doc: String { return "Movement" }
func move(vector: CGVector) {
print("moved \(vector)")
}
}
class Physics: Component {
override var doc: String { return "Physics" }
func update(time: NSTimeInterval){
print("updated at \(time)")
}
func move(vector: CGVector) {
print("updated \(vector)")
}
}
// an example factory
var entity: Entity? = Entity()
if let instance = entity {
// a couple of ways of adding components
var inputComponent = Input()
instance.add(inputComponent)
instance.add(Movement())
instance.add(Physics())
var m = instance.retrieve(Movement.self)
instance.subscribe(m!, method: .cgvector(m!.move), to: .onInput)
let p = instance.retrieve(Physics.self)!
instance.subscribe(p, method: .cgvector(p.move), to: .onInput)
inputComponent.goRight()
inputComponent.goUp()
instance.retrieve(Input.self)?.goRight()
instance.remove(Movement.self)
m = nil
inputComponent.goRight()
}
entity = nil //not a strong ref cycle
I fell into that situation but i found a cool solution
with an anonymous inline function, it is like mapping
here is an example
var cellConfigurator: ((UITableViewCell, _ index: IndexPath) -> Void)?
func setup<CellType: UITableViewCell>(cellConfig: ((CellType, _ index: IndexPath) -> ())?)
{
// this mini function maps the closure
cellConfigurator = { (cell: UITableViewCell, _ index: IndexPath) in
if let cellConfig = cellConfig, let cell = cell as? CellType {
cellConfig(cell, index)
}
else
{ print("-- error: couldn't cast cell") }
}
}

Resources