fbpx

Blogs from the Ranch

< Back to Our Blog

What Use are Swift Access Modifiers?

Avatar

Jeremy W. Sherman

If you’re writing Swift apps and not Swift frameworks, do Swift’s access
modifiers even matter, or are they just a big distraction from the task at
hand: Making your app do whatever it’s supposed to do?

As we’ll see, any app can get some mileage out of private and fileprivate.
For a small app, the other 60% of access modifiers are unnecessary line noise.
As app and team size increase, the line between “subsystem,” “reusable
component,” and “framework” blurs, and recognizing this by creating frameworks
and using the full spectrum of access modifiers might pay off in preventing
unwanted, ossifying coupling throughout your magnum opus.

Framework Devs Love Access Modifiers

Framework developers have a vested interest in keeping people from using
anything they haven’t explicitly greenlighted as something they’re willing to
support until the end of days. Public API ties their hands—any backwards
incompatible changes take a full deprecation period to implement, and depending
on usage, not even that might be enough—so they want to be very careful
about what they make public.

Swift plays along with this by defaulting to internal access, so that no one
outside your module can touch anything unless you’ve explicitly marked it as
public. And the language’s focus on access control remains strong: Where
Swift 2 supported
three flavors of access modifier,
Swift 3 pushes this to five flavors.
Swift Evolution proposal SE-0025, “Scoped Access Level,”
split private into private and fileprivate, and then
SE-0117, “Allow distinguishing between public access and public overridability,”
further split public into public and open,
leaving Swift 3 with a range of access modifiers starting from private,
continuing through fileprivate, internal, and public, and finishing with
open.

App Dev Access Control Is Just a Speedbump

But App devs are writing code for their own use. If they can’t use it, they can
change it so they can. If they don’t need it, they can just delete it. Is that
handy method marked private? Well,
find . -name '*.swift' -print0 | xargs -0 sed -i '' -e 's/private //',
and not any more!

But Check Out These App Dev Tricks

So why, as an app dev, might you care to throw some speed bumps in your path?

Test-Free Refactoring Damage Control

If you change existing code, and you botch it, any callers might have their
behavior broken.

If that code is marked private or fileprivate, though, the damage is
contained: You know exactly what codepaths to exercise at a glance, because
they’re all right there, in the same file you’re already working in.

This is even more convenient when you decide to change behavior: You can just
up and delete something file/private entirely after a once-over to verify
nothing else in the file is touching it. Or you can rename it, or
what-have-you.

The key thing is: Marking something as file/private significantly lowers the
bar of due diligence when hacking up existing code. This saves you time and
worry.

Signaling Through the Diff-Peephole

The same “damage control” principle comes into play during code review, too.

GitHub and similar present just the changed lines and a couple more around them
for context. This leaves your reviewer seeing here a snatch of code, there
another few lines. Unless they’re intimately familiar with the code in
question, or they’re exceedingly diligent, they’ll find themselves with only
a very limited context to review your changes within.

If something’s marked
file/private, they know, even with this teensy window into your codebase,
that that thing’s usage is limited in scope.
They also might review it less intensely than API that is more public
than file/private,
since that access level keeps it a tucked-away detail of whatever they’re looking at – if
the naming of some extracted function and its parameters isn’t bang-on perfect,
the harm is very limited, and the bikeshedding can conclude early.

Forcing Factory Function Usage

You can also use file/private to force creation of a type to route through
a factory function. If the factory returns a protocol type, this can entirely
conceal the existence of the type from view: Talk about encapsulation!

As an example, Deferred
reserves the ability to create an IgnoringTask
to itself:

public struct IgnoringTask<Base: FutureType> where Base.Value: ResultType {
    public typealias Result = TaskResult<Void>

    fileprivate let base: Base
    fileprivate let cancellation: Cancellation

    fileprivate init(_ base: Base, cancellation: Cancellation) {
        self.base = base
        self.cancellation = cancellation
    }
}

The only way clients of this library can create one is
through calling ignored() on a TaskType:

extension TaskType {
    public func ignored() -> IgnoringTask<Self> {
        return IgnoringTask(self, cancellation: cancel)
    }
}

This is not the best example, since it is from the library/framework code camp
rather than the app code camp, but if you find yourself implementing some
tricky frameworkish code in the context of your app, this trick might yet come
in handy.

Big Apps Are Built of Little Frameworks

The line between app code and framework code blurs at scale.
If you have a few several-person subteams working on one app,
de facto frameworks tend to spring up, as one team vends API to another team,
or one person builds a component used and reused across the rest of the app.

At that point, the codebase’s agility might benefit from explicitly cutting out
hunks of app code as tiny frameworks and putting the full spectrum of access
modifiers to work.

When you find you can safely evolve a gnarly hunk of UI code already used
several hundred times across an app, you’ll thank your lucky stars those
framework-friendly features found their way into Swift!

Conclusion

Access control modifiers in Swift are just speedbumps for a small team working
on a small app. That team will want to ignore them as a distraction, but
might yet get some communication value—and peace of mind—out of using
private and fileprivate.

For a framework developer (both those intending to be framework developers,
and those accidentally falling into that role in the context of a titanic
app), the full spectrum of access modifiers can usefully be deployed.

Avatar

Jeremy W. Sherman

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project