Swift Imports

If you’ve been developing applications in Swift for any length of time,
you’ve grown accustomed to often having one or more lines of text at the top of
your file. No, I’m not talking about the Xcode generated commented-out metadata
when you make a new Swift file (if you’re like me and don’t particularly care
for it either, we’ve got you covered)
— I’m talking about our friend the import declaration.

In our first example we’re just importing the UIKit module as usual — no
funny business going on there. In our second example, we’re actually importing
the entire UITableViewControllersubmodule, whereas in our last example
we’re explicitly importing only a class that has the symbol name
of UITableViewController.

If we inspect the submodule UITableViewController we see it imports the
entire umbrella header for UIKit:

importFoundationimportUIKitimport_SwiftUIKitOverlayShims//// UITableViewController.h// UIKit//// Copyright (c) 2008-2017 Apple Inc. All rights reserved.//// Creates a table view with the correct dimensions and autoresizing, setting the datasource and delegate to self.// In -viewWillAppear:, it reloads the table's data if it's empty. Otherwise, it deselects all rows (with or without animation) if clearsSelectionOnViewWillAppear is YES.// In -viewDidAppear:, it flashes the table's scroll indicators.// Implements -setEditing:animated: to toggle the editing state of the table.@available(iOS 2.0, *)openclassUITableViewController:UIViewController,UITableViewDelegate,UITableViewDataSource{...

Hence why when we do not specify the import kind while importing
UITableViewController, we’re actually still just importing the entirety
of UIKit.

Although there are limited cases when you may explicitly need to specify the
import kind and symbol name, it does help remove excessive symbols you may not
need in the particular file you’re working in. As far as performance and binary
size goes, any unused symbols are already optimized out of the final binary by
the Swift compiler. If there’s no reference to it at compile time then it’s
removed, meaning that importing a framework but not using particular parts
of it shouldn’t have any negative implications.

Besides decluttering Xcode’s code completion, there is one scenario where it
does offer a distinct advantage: modules which both define a particular type
of the same name.

Consider two frameworks, FrameworkA and FrameworkB. For the sake of
simplicity, they both implement a struct called Foo, but have different
behavior. What happens if we import both frameworks?

Both local variables are of type Foo, but come from different modules. This
could be confusing…particularly if we didn’t intend on using FrameworkA’s
implementation of Foo. What can we do if we only wanted to use a different
part of the framework?

importstructFrameworkA.ThingAimportFrameworkBfuncexample2(){letmyFoo=Foo(baz:3)// Error: Now FrameworkA's definition of Foo is not in scopeletmyFooAgain=Foo(bar:"test")// Still Using FrameworkB's implementationletthing=ThingA()// But we still have access to ThingA from FrameworkA!myFooAgain.doThingFromFrameworkB()}

Now there’s no longer two Foos in scope, and we’re explicitly only importing
what we intend to use from FrameworkA. Nice!

Before we conclude our spotlight on Swift import declarations, browsing the
Swift documentation led me to an interesting declaration attribute that’s
currently not officially released: @_exported

According to the docs,
applying this attribute to an import declaration exports the import module,
submodule, or declaration from the current module. For example, consider if
FrameworkA had the import declaration @_exported import FrameworkC. In
our application we could then only import FrameworkA and still be able
to access FrameworkC thanks to the @_exported attribute.

But considering that it’s a private Swift attribute (as denoted by that pesky
underscore 😒 ), we’re probably best off not using it for the time being.