Swift/Objective-C Selector Word Tricks

Hello All!

I've been working with some Swift and Objective-C code and I've noticed some interesting things.

  1. Calling an initWith[X] method from Swift as just init([x]: ...) works like a charm
  2. Many selectors named shared[X] or default[X] can be called in Swift by just shared or default (see FileManager for example).
  3. In some cases, a function [x]For[Y](...) may be able to be called as [x](for: ...) (although this seems to potentially require an Objective-C header)

I'm curious if there is any nice list or common place where all these word tricks (not sure what else to call them) can be seen (i.e. how they work, what other ones there are).

Also, are item #2 and #3 possible by defining a protocol in Swift and casting an object to it (as opposed to using an Objective-C header)?

1 Like

The rules are documented in prose across these documents:

And most of the C++ compiler-side implementation lives here: swift/lib/ClangImporter/ImportName.cpp at main · swiftlang/swift · GitHub

4 Likes

Nice! Thanks for the note.

To reiterate my second question, is it possible to emulate these purely in Swift?

For example:

@objc protocol SomeThingP: NSObjectProtocol {
  static sharedThing: Self { get }
}

let SomeThing = unsafeBitCast(NSClassFromString("SomeThing")!, to: SomeThingP.Type.self)

let thing1 = SomeThing.shared // :( Doesn't work
let thing2 = SomeThing.sharedThing // :) Does work

You can use the @objc attribute to provide a different name in Objective-C. For example:

@objc protocol SomeThingP: NSObjectProtocol {
  @objc(sharedThing) static var shared: Self { get }
}

Note that "sharedThing" needs to be known statically at compile time, so it can't vary across different types that conform to this protocol, but you can add an additional @objc attribute in the class to give it a second Objective-C name:

@objc class Potato: NSObject {
  ...
}

extension Potato: SomeThingP {
  @objc(sharedPotato) static var shared: Self {
    ...
  }
}
4 Likes

Oooh nice! Thanks again. Does this work for function declarations as well (e.g. [x]For[Y](...) -> [x](for: ...)?

1 Like

The @objc attribute works for (class and instance) member functions of classes, yep.

3 Likes

Thanks! I figured out the syntax. It's essentially

@objc([x]For[Y]:) func [x](for: ...)
1 Like