NSNotificationCenter passing structs as part of the UserInfo
Asked Answered
I

1

18

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
Incorruptible answered 2/3, 2015 at 11:37 Comment(0)
K
32

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.

Knudsen answered 25/4, 2015 at 13:34 Comment(4)
I've never used generics so took me some time trying to determine how to get my Struct back. I used the notification object but the concept is the same if let wrapperItem = notification.object as? Wrapper<YourStruct> { let foo = wrapperItem.wrappedValue }Wyndham
How Can I grab the value when I am at the userinfo Dictionary on the AddObserver?Murry
let editedStruct = payload.userInfo?["approval"] as? Wrapper<aStruct> and to see the value print((editedStruct?.wrappedValue.aValue)! as String) @WyndhamMurry
Except for using a wrapper, maybe returning a wrapped object with extension function is good, tooBelovo

© 2022 - 2024 — McMap. All rights reserved.