Home » Swift » NSNotificationCenter passing structs as part of the UserInfo

NSNotificationCenter passing structs as part of the UserInfo

Posted by: admin November 30, 2017 Leave a comment

Questions:

Due to NSNotificationCenter.defaultCenter().postNotificationName userinfo only accepting dictionaries with data complying with AnyObject protocol, does anyone have any suggestions how to post structs as part of an NSNotification?

My initial thought it is to wrap the struct in a class – but then what would be the point in using a struct in the first place.

Am I missing something or is this just a result of conflating Swift with API’s built for Objective C?

Here’s a demonstration of what I’m describing: –

class wrapper: NSObject {

  var aStructToWrap: aStruct

  init(theStruct: aStruct) {

    aStructToWrap = theStruct

    super.init()
  }

}

struct aStruct {
    var aValue: String
}

let aRealStruct = aStruct(aValue: "egg")


NSNotificationCenter.defaultCenter().postNotificationName("aKey", object: nil, userInfo: ["anotherKey": aRealStruct]) // ERR: Extra argument 'userinfo' in call

let wrappedStruct = wrapper(theStruct: aRealStruct)

NSNotificationCenter.defaultCenter().postNotificationName("aKey", object: nil, userInfo: ["anotherKey": wrappedStruct]) // no error
Answers:

The issue is that the original Obj-C method requires an NSDictionary, which only takes object types as keys and values, which translates to [AnyObject: AnyObject] in Swift, except NSDictionary likes to compare its keys with isEqual: which is in the NSObject protocol, so the key must be an NSObject (I don’t know if NSObjectProtocol was sufficient but Apple has decided to make it an NSObject).
Therefore, the NSDictionary userInfo must be [NSObject: AnyObject] in Swift, and so you can’t put a struct in there, and I don’t believe you could in Objective-C either.

Sadly a wrapper will be necessary. We could play with NSValue and produce something ugly and inefficient, but in any case the best solution is the wrapper you have created.

However, you made a subclass of NSObject, that wasn’t needed, so you can throw that code away 🙂

class Wrapper {
    var aStructToWrap: aStruct
    init(theStruct: aStruct) {
        aStructToWrap = theStruct
    }
}


struct aStruct {
    var aValue: String
}

Except we can do even better! We can make a generic wrapper for any struct or value (or even object) you like.

class Wrapper<T> {
    var wrappedValue: T
    init(theValue: T) {
        wrappedValue = theValue
    }
}

struct aStruct {
    var aValue: String
}

let aRealStruct = aStruct(aValue: "egg")

let wrappedStruct = Wrapper(theValue: aRealStruct)

NSNotificationCenter.defaultCenter().postNotificationName("aKey", object: nil, userInfo: ["anotherKey": wrappedStruct]) // no error

That’s a mutable wrapper, feel free to make it immutable by switching the var for a let.