Translation: Yu Jin; Proofreading: Numbbbbb, Yousanflics; Finalized: Pancf

The first lesson we learn as software developers is how to organize concepts and functions into separate units. At the smallest level, this means thinking about types, methods, and attributes. These things form the basis of modules, which in turn can be packaged as libraries or frameworks.

In this way, the import declaration is the glue that holds everything together.

While the import statement is important, most Swift developers are familiar with its most basic uses:

import <#module#>
Copy the code

This week in NSHipster, we’ll explore other uses of Swift’s most important feature.


Import declarations allow your code to access symbols declared in other files. However, if multiple modules declare a function or type with the same name, the compiler will not be able to tell which one your code wants to call.

To illustrate this problem, consider the Triathlon and Pentathlon modules, which represent multi-sport competitions:

The triathlon consists of three events: swimming, cycling and running.

Func swim() {print("🏊 swim 1.5 km")} func bike() {print("ðŸšī Cycle 40 km")} func run() {print("🏃 run 10 km") }Copy the code

The ironman pentathlon module consists of five events: fencing, swimming, equestrian, shooting and running.

Func fence() {print("ðŸĪš Bout with epees ")} func swim() {print("🏊 swim 200 m")} func ride() {print("🏇 Func shoot() {print("ðŸŽŊ shoot 5 targets")} func run() {print("🏃 run 3 km cross-country") }Copy the code

If we import one of the modules individually, we can reference each of their functions with their unqualified name with no problems.

Import Triathlon swim() // Correct, call Triathlon. Swim bike() // correct, call Triathlon. Bike run() // Correct, call TriathlonCopy the code

However, if we import two modules at the same time, we cannot use all unqualified function names. Triathlon and pentathlon both include swimming and running, so the reference to swim() is ambiguous.

Import Triathlon import Pentathlon bike() // Correct, call Triathlon. Bike fence(Copy the code

How to solve this problem? One strategy is to use a fully qualified name to handle any ambiguous references. By including the module name, there is no confusion as to whether the program wants to swim laps in a pool or a mile in open water.

Import Triathlon import Pentathlon triathlon.swim () // Correct, pointing to a fully qualified reference to triathlon.swim () // Correct, A fully qualified reference to Pentathlon. SwimCopy the code

Another way to resolve API name conflicts is to change the import declaration to be more selective about what needs to be included for each module.

Import a single declaration

The import declaration provides a style for specifying the import of individual constructs, classes, enumerations, protocol and type aliases defined at the top level, as well as functions, constants, and variables.

import <#kind#> <#module.symbol#>
Copy the code

Here, <#kind#> can be any of the following keywords:

Kind Description
struct The structure of the body
class class
enum The enumeration
protocol agreement
typealias Type the alias
func function
let constant
var variable

For example, the following import declaration adds only the Swim () function of the Pentathlon module:

Import func pentathlon.swim swim() // Correct, call pentathlon.swim fence() // error, cannot parse flagCopy the code

Resolve symbolic name conflicts

When multiple symbols in code are referenced by the same name, the Swift compiler resolves the reference in order of priority with reference to the following information:

  1. Local declaration
  2. Declaration of a single import
  3. The whole imported module

If there are multiple candidates for any one priority, Swift will not be able to resolve the ambiguity, resulting in a compilation error.

For example, a whole imported Triathlon module provides swim(), bike(), and run() methods, but a single imported Swim () function declaration from Pentathlon overrides the corresponding functions in the Triathlon module. Similarly, the locally declared run() function overrides the symbol of the same name in Triathlon, as well as any single imported function declaration.

Import Triathlon import func Pentathlon. Swim // The local function will cover the whole imported Triathlon module func run() {print("🏃 run 42.195km ")} Swim () // Correct, call Pentathlon. Swim bike() // correct, call Triathlon. Bike run() // correct, call local runCopy the code

What is the result of this code? A wacky multi-sport event that includes laps of swimming in a pool, a moderate bike ride, and a marathon run. (@us, Iron Man)

If a local or imported declaration conflicts with the name of the module, the compiler first looks for the declaration and then performs a qualified lookup in the module.

import Triathlon

enum Triathlon { case sprint, olympic, ironman }

Triathlon.olympic // references the local enumeration case triathlon.swim () // references the module’s function

The Swift compiler does not notify the developer or mediate naming conflicts between modules and local declarations, so you should be aware of this possibility when using dependencies.

Clarify and narrow the scope

In addition to resolving naming conflicts, import declarations can serve as a way to clarify programmer intentions.

For example, if you are using only one function from a large framework like AppKit, you can specify that function separately in the import declaration.

import func AppKit.NSUserName

NSUserName() // "jappleseed"
Copy the code

The source of top-level constants and variables is often harder to identify than other import symbols, and this technique is especially useful when importing them.

For example, among the many features provided by the Darwin Framework is a top-level stderr variable. An explicit import declaration here can forego any questions about the variable’s origin during code reviews.

import func Darwin.fputs import var Darwin.stderr struct StderrOutputStream: TextOutputStream { mutating func write(_ string: String) { fputs(string, stderr) } } var standardError = StderrOutputStream() print("Error!" , to: &standardError)Copy the code

The import modules

The final import declaration style provides another way to limit API exposure.

import <#module.submodule#>
Copy the code

You will most likely encounter submodules in large system frameworks such as AppKit and Accelerate. While this umbrella framework is no longer a best practice, they played an important role in Apple’s transition to Cocoa in the early 20th century.

For example, you can import only the DictionaryServices submodule of the Core Services Framework, thus isolating your code from numerous deprecated apis such as Carbon Core.

import Foundation import CoreServices.DictionaryServices func define(_ word: String) -> String? { let nsstring = word as NSString let cfrange = CFRange(location: 0, length: nsstring.length) guard let definition = DCSCopyTextDefinition(nil, nsstring, Cfrange) else {return nil} return String (definition) takeUnretainedValue ())} define (" apple ") / / "apple | ˈ ap goes l | noun 1 the round fruit of a tree..."Copy the code

In fact, separate imported declarations and submodules don’t do any real good other than clarify the programmer’s intent. This doesn’t make your code compile any faster. Since most of the submodules seem to reimport their umbrella headers, this doesn’t reduce the noise on the autocomplete list.


As with many arcane advanced topics, the reason you haven’t heard of these import declaration styles is probably because you don’t need to. If you’ve developed a lot of apps without them, you can be reasonably confident that you don’t need to start using them.

Instead, the more valuable takeaway here is to understand how the Swift compiler resolves naming conflicts. To do this, it is important to understand import declarations.

This article is translated by SwiftGG translation team and has been authorized to be translated by the authors. Please visit swift.gg for the latest articles.