Swift Regex Deep Dive
iOS MacOur introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
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 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
.
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!
So why, as an app dev, might you care to throw some speed bumps in your path?
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.
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.
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.
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!
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.
Our introductory guide to Swift Regex. Learn regular expressions in Swift including RegexBuilder examples and strongly-typed captures.
The Combine framework in Swift is a powerful declarative API for the asynchronous processing of values over time. It takes full advantage of Swift...
SwiftUI has changed a great many things about how developers create applications for iOS, and not just in the way we lay out our...