Inadvertently Extending Private API

The following code appears to silently shadow and preempt hidden private system API. This causes hard to detect and diagnose bugs when the new function uses different semantics than the original and the system code starts calling the extension, rather than the correct system function. From what I can tell, this is an encouraged design pattern, but it seems particularly fragile. Worse still, any future changes to the system base class introduce a whole new spin on the “fragile base class” problem when suddenly client code is extending and hiding a function that didn’t exist when the client code was written.


Is there something I’m missing here, or should be doing differently? One would hope access control or a namespace would prevent the extension of a function another module depends on, but that appears not to be true. The fact this happens silently is troubling, but the fact it seems possible after release to existing production code via a system software update seems far worse.


Thanks,

Nick


import HomeKit
extension HMHome
{
     func roomWithUUID(inUUID:NSUUID) -> HMRoom?
     {
          for room in self.rooms {
               if room.uniqueIdentifier == inUUID {
                    return room
               }
          }
          return nil
     }
}

Replies

Swift is designed to avoid this sort of problem with Swift classes, but Cocoa classes follow Obj-C rules, and it's been a longstanding weakness that no such safety applies there.


You have to think defensively about this. You should regard extending system classes as a terrible idea in general. When you must do it, the simplest approach is to prefix each of your added methods with a unique prefix, just as you should do for Obj-C class names. This is described here:


https://developer.apple.com/library/prerelease/mac/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html


under the subheading "Avoid Category Method Name Clashes".


Note that in many situations, the arrangement of code tends to provide an alternative "home" for convenience methods like the one you've shown. For example, you may have some kind of wrapper object that coordinates access to HomeKit anyway. In those situations, it's often pretty natural to add the convenience method to a wrapper or utility class, without sacrificing readability of code that uses it.

Okay thanks, I was afraid of that. FWIW, it seems to be a design pattern I'm seeing more in public code from Apple, without any kind of safeguards. I know Obj-C categories include names (although I don't know that they're used anywhere) and I was hoping Swift might provide some kind of namespace solution to this problem.

FWIW, it seems to be a design pattern I'm seeing more in public code from Apple, without any kind of safeguards.

Apple has a pretty clear warning about this, as referenced by QuinceyMorris. If you see sample code that doesn’t follow this guideline, please file a bug against it. I’d appreciate you posting the bug number here, just for the record.

I know Obj-C categories include names (…) and I was hoping Swift might provide some kind of namespace solution to this problem.

It does, but only for Swift classes. It’s hard to introduce namespace into the Objective-C world without breaking a lot of stuff.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"