Categories

# You can use Swift’s “private” access modifier to limit the reach of overrides and extensions, but not monkeypatches

### What happens when you mark operator overload definitions as `private`?

While I was translating the code in the RayWenderlich.com article Sprite Kit Tutorial for Beginners from its original Objective-C to Swift, I got to the point where he provides some routines to do vector math on `CGPoint`s. These are “standalone” utility functions and aren’t methods inside any class.

The first three functions are for vector arithmetic:

• Adding two vectors together: (x1, y1) + (x2, y2) = (x1 + x2, y1 + y2)
• Subtracting on vector from another: (x1, y1) – (x2, y2) = (x1 – x2, y1 – y2)
• Multiplying a vector by a scalar: (x, y) * k = (k * x, k * y)

Here’s his original code, written in Objective-C:

I’ve always found methods like `add(firstThing, secondThing)` a little clunky-looking; I’d much rather write it as `firstThing + secondThing`. Since Swift supports operator overloading (Objective-C doesn’t), I decided to implemented the `rwAdd()`, `rwSub()`, and `rwMult()` functions as overloads of the `+`, `-`, and `*` operators. Here’s my equivalent Swift code:

Note that I added a `private` access modifier to each of the functions above. As I pointed out in my last Swift article, Swift’s access modifiers are based on files and modules, not the class hierarchy. In Swift, any entity marked `private` is visible and accessible within its own file, and invisible and inaccessible from outside its own file. I wanted to see what would happen to operator overloads.

Here’s what I found:

Operator overloads marked `private` are available within the file where they’re defined, and are not available outside that file. Outside the file where the `private` operator overloads were defined, any attempt to use them results in an error.

A little testing confirmed that I could add and subtract `CGPoint`s and multiply them by scalars using the `+` operation from inside the same file, but couldn’t do so from inside in other files.

### What happens when you mark extensions as `private`?

The next couple of functions that were in Sprite Kit Tutorial for Beginners were for getting and normalizing the length of a vector. Here’s the original Objective-C code:

Swift supports the addition of functionality to types by means of extensions. I thought that implementing these functions as computed properties of `CGPoint` in an extension would make for more elegant code — I’d rather write `vector.length` and `vector.normalized` than `rwLength(vector)` and `rwNormalize(vector)`. Here’s what I wrote:

Note that I marked the entire extension as `private`. How would that affect their visibility inside and outside the file where the extension was defined?

The members of extensions marked `private` are available within the file where they’re defined, and are not available outside that file. Outside the file where the `private` extension members were defined, any attempt to use them results in an error, and auto-complete wouldn’t even list them.

Once again, testing confirmed that I could normalize and get the length of vectors represented by `CGPoint`s from inside the same file, but couldn’t do so from inside in other files.

### What happens when you mark “monkeypatches” as private?

Marking overrides and extensions as private led me to wonder what would happen if I marked a “monkeypatch” as private.

Monkeypatching is a term that’s used in the Python and Ruby developer communities, and it means dynamically replacing an existing class method with one of your own. It’s takes some effort to do in Objective-C (thanks to Joe Smith for the heads-up!), but it’s quite simple to do in Swift — and not just to methods, but properties as well. Here’s a quick example, in which I redefine the `String` class property `utf16Count`, which returns the number of UTF-16 characters in a string (it maps to `NSString`‘s `length` method):

With this extension, I’ve monkeypatched `utf16Count` so that it always returns `5`, no matter what the number of UTF-16 characters in the string is.

Monkeypatching is powerful, and it’s sometimes useful, but as smarter people than I have pointed out, it can create more problems than it solves. As Jeff Atwood asked in Coding Horror, “Can you imagine debugging code where the `String` class had subtly different behaviors from the `String`you’ve learned to use?”

That’s what got me thinking: what if `private` could be used to limit the scope of a monkeypatch, to limit the applicability of my warped verstion of `utf16Count` to a single file? Is such a thing possible? To answer these questions, I created this extension to `String` in one file:

This extension provides two things:

• A monkeypatch for `String`‘s already-existent `utf16Count` property so that it always returns the value `5`
• A new property for `String` called `someNumber`, which always returns the value `10`

In the same file where I defined my extension to `String`, I wrote this code:

And in another file, I defined the `doOutsideCount` method:

If making the extension `private` limited my redefinition of `utf16Count` to the file where I redefined it, the output of `doOutsideCount()` would be “UTF-16 count outside: 14”, since there are 14 UTF-16 characters in `testString`. I noticed that while typing in the code in this file, `utf16Count` was available to me in auto-complete.

Here’s the output that appeared on the console when I ran the app:

It appears that monkeypatching (redefining an existing member) in an extension marked `private` does not limit the monkeypatch to the file where the monkeypatch was defined. You can’t use `private` to limit the scope of a monkeypatch to a single file.

I changed the code in both files to try calling on `someNumber`, a method that didn’t already exist in `String:`

This wouldn’t even compile. Calling on the `someNumber` property of `String` outside of the file where I defined the private extension raises an error: `'String' does not have a member named 'SomeNumber'`.

My conclusion from this little bit of experimenting is also this article’s title:

You can use Swift’s `private` access modifier to limit the reach of overrides and extensions, but not monkeypatches.

## 2 replies on “You can use Swift’s “private” access modifier to limit the reach of overrides and extensions, but not monkeypatches”

Joe Smithsays:

You can Monkey Patch in Objective-C using some of the exciting stuff declared in objc/runtime.h.

E.g., class_getInstanceMethod and class_exchangeImplementations. :)

Joe Groffsays:

This isn’t really monkey-patching in the ObjC or Ruby sense. You’re declaring a new definition of ‘utf16count’, which shadows other definitions, but doesn’t change any behavior of other code at runtime. Swift extensions are designed not to impact code in other modules, so that they can be safely used without having to worry about clobbering extensions from other modules. The ‘private’ extension being visible across files is a bug which appears to be fixed in Swift 2.