TIL about @nonobjc
27 Feb 2016In my free time, I run a Twitter account called Public Extension, where I share handy Swift extensions I’ve found or come up with. A recent extension involved adding custom subscripts to NSUserDefaults
.
4️⃣6️⃣: Make NSUserDefaults a bit easier to work with 😃https://t.co/1eULFysnXb pic.twitter.com/7127Y6PCB1
— Public Extension (@PublicExtension) February 24, 2016
One attribute that sticks out is @nonobjc
. Often times, you’ll need to use @objc
1 to expose your Swift classes that don’t derive from an Objective-C class to the Objective-C runtime. It’s rare to explicitly cut that interoperability. Let’s dig into why this attribute is necessitated here by dropping it and deconstructing the compilation error:
The full error reads “Subscript getter with Objective-C selector ’objectForKeyedSubscript:
’ conflicts with previous declaration with the same Objective-C selector” (and the analog for the setter). Turns out that Objective-C also supports custom subscripting and Swift will automatically bridge them back! However, in our example, we’re overloading the subscript with the same key and different return types (also referred to as return type polymorphism), which Objective-C doesn’t support 😔. To fix this, we simply prevent the default bridging via @nonobjc
.
So, there you have it! A real example of the @nonobjc
attribute in action. Hope this post sheds some light on why it exists 😃
Shout-out to Joe Groff for the help and Garric Nahapetian for motivating me to write this post!
@jasdev Overloading will work if you mark one or both subscripts as @nonobjc.
— Joe Groff (@jckarter) February 21, 2016
@PublicExtension This looks really fun. I would love to hear more about this solution. Is there a blog post about it?
— Garric G. Nahapetian (@garricn) February 25, 2016
Footnotes:
-
Swift also allows for the
@objc(name)
variant, which usesname
when exposing the target class to the Objective-C runtime. ↩