12

When I'm implementing NSCoding protocol in Objective-C, I'd like to use NSStringFromSelector(@selector(name)) to get the key path of a property, like below

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:self.accountName forKey:NSStringFromSelector(@selector(accountName))];
    [aCoder encodeObject:self.userId forKey:NSStringFromSelector(@selector(userId))];
}

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super init];
    if (self) {
        _accountName = [aDecoder decodeObjectForKey:forKey:NSStringFromSelector(@selector(accountName))];
        _userId = [aDecoder decodeObjectForKey:forKey:NSStringFromSelector(@selector(userId))];
    }
    return self;
}

I like this way because it prevents from mistyping without the need to define lots of string constants, and I will get warnings if I renamed those properties. But I couldn't find a way to do this in Swift, any ideas?

Pride Chung
  • 573
  • 7
  • 20

1 Answers1

1

In Swift, selectors are strings. (Well, there's the Selector type, but that transparently converts to and from String.) So you can omit NSStringFromSelector and @selector and just use a string literal.

If you're looking to introspect the names of your properties... it's a bit tougher. Using reflect() on an instance of your class returns an object of type Mirror. (Paste one of those symbols in your playground and cmd-click to get the declarations in the standard library "header".) You can use that to walk the class's list of properties.

This would make your "encode everything" method look something like this:

func encodeWithCoder(coder: NSCoder!) {
    let mirror = reflect(self)
    let (accountNameKey, _) = mirror[0]
    let (userIdKey, _) = mirror[1]

    coder.encodeObject(accountName, forKey: accountNameKey)
    coder.encodeObject(userId, forKey: userIdKey)
}

It's not ideal, since you have to know the order of the properties' definitions. But it might be improved upon...

rickster
  • 124,678
  • 26
  • 272
  • 326
  • 8
    The problem is, he needs the `@selector()` syntax for verification. If he just puts a string, he can have a typo an and he won't have a warning. – Léo Natan Jun 09 '14 at 16:41
  • The real issue is swift has no direct replacement for this behaviour yet. – James Campbell May 28 '15 at 19:48
  • 1
    As of swift 2.2, you can new use special syntax for obtaining a selector. For example `let sel = #selector(UIView.insertSubview(_:at:)) // produces the Selector "insertSubview:atIndex:"`. Reference: https://github.com/apple/swift-evolution/blob/master/proposals/0022-objc-selectors.md – Mrwerdo Mar 13 '16 at 05:08
  • Pretty sure that doesn't help with getting property names, though — you need a function reference for `#selector`, and IIRC there's as yet no way to get a function reference for a property's implicit getter. – rickster Mar 13 '16 at 05:14
  • Waiting for an answer on this myself. Would prefer to use something like @selector(key) in Swift so that the compiler catches typos. – Ben Guild Apr 14 '16 at 09:24
  • 1
    There's now a [swift-evolution proposal](https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md) for getting compile-time-checked strings for property names. – rickster Apr 14 '16 at 13:39