Modules and Module Maps — Deeper Coverage
Go beneath the @import statement you already know how to write and understand the module system itself: what a Clang module actually is as a compiled artifact, how a module.modulemap file describes a framework's public interface, what the module cache stores and why it invalidates, and why this machinery is what makes Swift-Objective-C interop as smooth as it is.
An earlier lesson, 'Modules and Frameworks,' taught you the practical, day-to-day usage of modules: write @import Foundation; (or let Xcode's modern build settings do it for you implicitly), get a framework's API, move on. That lesson was about usage. This one is about mechanics — the actual system underneath that single line. Understanding the mechanics matters for reasons that go well beyond trivia: it explains why modular builds are faster than textual-include builds, why a stray macro in one header used to silently break an unrelated header before modules existed, what's actually being cached on disk in your DerivedData folder and why cleaning it sometimes 'fixes' inexplicable build errors, and — perhaps most importantly for anyone working in a mixed Swift/Objective-C codebase — why Swift is able to import an Objective-C framework's entire API as a coherent, native-feeling Swift interface instead of a pile of loose C declarations. If the earlier module lesson was 'how to drive the car,' this lesson is 'how the engine works,' and cross-references 'Swift Interoperability' and 'Bridging Headers' at the point where module mechanics directly explain how that interop is implemented.
What a Clang module actually is, versus textual inclusion
Before modules existed, every #import (or #include) was fundamentally a textual operation: the preprocessor found the named header file, read its contents, and pasted that text — verbatim, token for token — into your source file at the point of the #import, exactly as if you had typed the header's contents there by hand. #import differs from plain C's #include only in that it tracks which files it has already pasted in for this translation unit and refuses to paste the same file in twice, which is what prevents the classic double-inclusion problem without needing manual include guards. But the fundamental operation — dump text, then let the compiler parse that text along with everything around it — is unchanged from #include.
This textual model has two real costs. The first is compile time: if ten different .m files in your project each #import <Foundation/Foundation.h>, the compiler re-parses the entirety of Foundation's headers — thousands of declarations — ten separate times, once per translation unit, because each translation unit is compiled independently and each one re-pastes and re-parses the same text from scratch. The second cost is more subtle and more dangerous: because textual inclusion is just pasting, any #define macro active at the point a header is included can silently change how that header's declarations are interpreted, and a header can itself #define a macro that then leaks forward and corrupts the meaning of every header included after it in the same file. A macro named MIN or MAX defined in one header, for instance, can silently shadow a same-named function or macro expected by a completely unrelated header included later — a real, historically common class of bug in large C and Objective-C codebases.
A Clang module solves both problems by replacing textual pasting with a semantic representation. When the compiler builds a module for the first time, it parses the module's headers exactly once and serializes the resulting semantic information — the actual declarations, types, and their meanings, not the source text — into a compact, precompiled on-disk form (a .pcm file, for 'precompiled module'). Every subsequent translation unit that imports that module simply loads this already-parsed, already-typechecked representation instead of re-parsing header text from scratch. Because what's loaded is the finished semantic result rather than raw text, macros defined in your importing file have no way to reach backward and change how the module's own headers were interpreted — the module was already fully parsed and locked in before your file's macros existed. This is the core reason modules are simultaneously faster to compile and immune to the macro-leakage problems that plagued large textual-include codebases for decades.
Framework modules and the module.modulemap file
A 'framework module' is simply a Clang module whose contents are defined by an Apple-style .framework bundle rather than a loose collection of headers. Most system frameworks (Foundation, UIKit, CoreGraphics) and most third-party frameworks you link against are framework modules, and the thing that actually declares 'this framework is a module, and here is exactly what belongs to it' is a small text file named module.modulemap, conventionally placed inside the framework bundle's Modules subdirectory (MyFramework.framework/Modules/module.modulemap).
The module map's syntax is deliberately simple. A minimal one for a framework named Networking might read: framework module Networking { umbrella header "Networking.h" export * module * { export * } }. The framework module keyword pair declares that this is a framework-style module named Networking; the umbrella header directive names the single header that, by convention, is expected to #import every other public header the framework wants to expose (this mirrors the umbrella-header convention you'd already recognize from building any Objective-C framework, module map or not); and export * module * { export * } tells the compiler that everything reachable from that umbrella header should be re-exported as part of the Networking module's public interface, including anything the umbrella header itself imports from submodules.
Xcode generates this module.modulemap file for you automatically for any target with 'Defines Module' enabled in its build settings — which is why most iOS and macOS engineers go years without ever hand-writing one, even though every framework module they've ever used has one underneath it. You only tend to write or edit a module map by hand in less common situations: wrapping a plain C library that has no notion of Objective-C frameworks at all, or fine-tuning exactly which headers of a complex framework are and aren't exposed as part of its module interface.
Umbrella headers and the public/private header distinction
An umbrella header is the single 'front door' header for a framework — conventionally named after the framework itself (Networking.h for a framework named Networking) — whose entire job is to #import every other header the framework wants client code to be able to see. When a module map says umbrella header "Networking.h", it's telling the module system 'walk this header, and everything it transitively imports is what this module contains.' This is exactly the same umbrella-header convention that predates modules entirely (it's how a framework's Xcode-generated top-level header has always worked), but the module map gives it new significance: it's now also the literal definition of the module's boundary, not just a developer convenience for a single #import <Networking/Networking.h>.
Framework bundles also distinguish between public headers and private (or project) headers, a distinction you configure per-header in the target's 'Headers' build phase. Public headers are the ones copied into the framework bundle's Headers directory and are the ones eligible to be listed in (or transitively reached from) the umbrella header — they form the framework's actual external API. Private headers are copied into a separate PrivateHeaders directory; they're visible to other targets in the same project or workspace that specifically opt in, but they are deliberately excluded from the umbrella header and therefore excluded from the module's public interface, meaning ordinary client code that only does @import Networking; cannot see or use anything declared in a private header at all, module-wise.
This public/private split is how a framework author draws a clean line between 'the API I promise to support and keep stable' and 'internal implementation detail I need visible across a few of my own files but don't want the outside world depending on.' Before modules, this distinction existed but was weaker — private headers could still technically be #imported directly by path if someone really wanted to, bypassing the framework's intended boundary. The module system reinforces the boundary more strongly: because @import Networking; only ever resolves to what the module map's umbrella header (and its public-header contents) actually exposes, a private header that was never folded into that umbrella simply isn't part of the module at all, and normal client code has no module-level path to reach it.
@import versus #import: two different operations wearing similar syntax
It's easy to assume @import Foundation; is just a slightly different spelling of #import <Foundation/Foundation.h>, but they trigger genuinely different compiler behavior. #import, as covered above, is always a textual operation — even when the target framework happens to also be built as a module, plain #import still means 'find this header file and paste its text,' and it's only because of a separate, opt-in compiler feature (implicit modules, usually enabled by the 'Enable Modules' build setting) that Clang is permitted to silently substitute a module load for what your source code wrote as a textual #import, transparently, without you changing a single line.
@import, by contrast, is explicit, unambiguous module syntax: @import Foundation; can never mean 'paste text' — it always means 'load the Foundation module,' full stop, regardless of any build setting. This is also why @import doesn't require (and doesn't respect) a matching #ifndef/#define header guard the way a textual header does — a module either has already been loaded for this compilation, in which case re-stating @import is a harmless, cheap no-op, or it hasn't, in which case @import loads it once. There's no re-parsing risk to guard against because there's no re-pasting happening at all.
In modern Xcode-generated Objective-C code you'll frequently see plain #import <Foundation/Foundation.h> at the top of a file and yet the build is still going through the module system underneath, because 'Enable Modules (C and Objective-C)' is on by default for new targets — the textual #import syntax is preserved for source compatibility with decades of existing code, while the compiler transparently upgrades the actual behavior to a module load whenever the target being imported is available as a module. Writing @import directly is really only necessary (or stylistically preferred) in code that wants to be unambiguous that a module load, not a textual paste, is intended.
Module boundaries and what they actually enforce
A module's boundary is precisely the set of declarations reachable from its umbrella header (plus any submodules its map explicitly exports) — nothing more, nothing less. This has a very concrete practical consequence: symbols that exist in a framework's implementation but were never routed through a public header, through the umbrella header, and through the module map's export rules simply do not exist as far as any importer of that module is concerned, even if the underlying compiled binary technically contains the machine code for them. The module system turns 'this is a private implementation detail' from a polite convention (please don't #import this internal header) into something closer to an enforced interface, because there is no module-level path from @import Networking; to a symbol the module map never exported.
Submodules let a framework further subdivide its own module boundary — a module map can declare module Networking.Sockets { header "Sockets.h" export * } nested inside the top-level Networking module declaration, letting client code either @import Networking; to get everything, or the more targeted @import Networking.Sockets; to pull in only that submodule's slice of the API. This granularity is mostly used by very large system frameworks (you'll see it in Apple's own module maps for frameworks like Darwin or Dispatch) rather than typical application-level frameworks, but it's worth recognizing the dotted @import Framework.Submodule; syntax when you encounter it, since it's the same module system, just addressing a narrower boundary within one framework.
The module cache: what's stored, and why it invalidates
Every time Clang compiles a module for the first time under a particular set of compiler flags, it writes the resulting precompiled representation to disk in the module cache — on Apple platforms this typically lives under DerivedData's ModuleCache.noindex directory, and each cached module is stored as a .pcm file. The next time any translation unit imports that same module under the same flags, the compiler checks the cache first; if a valid, up-to-date .pcm already exists for that exact configuration, the compiler loads it directly instead of reparsing any header text at all, which is where the bulk of the compile-time savings from modules actually comes from in practice — the expensive header-parsing work happens once per distinct configuration, not once per translation unit.
The critical phrase there is 'under the same flags.' A module's compiled representation isn't just a function of its own header text — it's also a function of everything that affects how those headers would be interpreted: the active preprocessor macro definitions passed on the command line, the target architecture, the language dialect and standard version, optimization-irrelevant-but-semantics-relevant flags, and the deployment target, among others. Clang's module cache keys each cached .pcm by a hash of the relevant compiler flags, precisely so that a module compiled for one configuration is never mistakenly handed to a translation unit compiling under meaningfully different flags that could change what those headers actually mean.
This is exactly why changing certain build settings — flipping a preprocessor macro definition, switching a deployment target, adding a new architecture to the build — can suddenly trigger what looks like a large, slow rebuild even for code you didn't touch: the flag hash for that configuration no longer matches any existing cached .pcm, so Clang has to rebuild the module from scratch for the new configuration before your translation units can use it. This is also, incidentally, the mechanical reason 'clean the build folder / delete DerivedData' is such a common (if blunt) fix for mysterious module-related build errors: it discards every cached .pcm outright, forcing every module to be freshly rebuilt from its current header state rather than potentially loading a stale cached module left over from an earlier, inconsistent build configuration.
Modular headers and interoperability implications
A header is 'modular' when it's actually reachable through some module's map — in practice, essentially every Apple SDK header and every properly configured framework header qualifies. Being modular is what makes a header eligible to be loaded via the fast, cached module path at all; a header that exists outside any module map (a loose header dropped into a project with no framework structure and no module.modulemap) can still be #imported normally, it just always goes through the older textual path rather than ever benefiting from module caching, because there's no module for it to belong to.
This modular-headers property is also the direct mechanical foundation of Swift/Objective-C interoperability, which is why this lesson cross-references both 'Swift Interoperability' and 'Bridging Headers.' When Swift code writes import Networking, Swift isn't reading and reinterpreting Networking's raw Objective-C header text on the fly — it's loading Networking's already-built Clang module (the same .pcm-backed representation this section has been describing) and running it through the Objective-C-to-Swift import machinery, which translates the module's semantic declarations (already fully typechecked, already free of macro ambiguity) into an idiomatic Swift interface — NS_ENUM constructs become genuine Swift enums, nullable/nonnull annotations become Optional versus non-optional Swift types, and so on. This is precisely why Swift can import an entire large Objective-C framework as a single, coherent, natively-typed API rather than a loose pile of C-level declarations: it's building on the same clean, precompiled semantic module that the rest of this lesson has been describing, not re-deriving that structure from scratch out of raw text.
The older bridging-header mechanism, by contrast, is what lets a Swift target in the same project see your own project's non-modular Objective-C code — your own .h files that were never packaged as a framework module — by explicitly listing them in a single 'YourProject-Bridging-Header.h' file that Swift processes textually rather than as a module import. Understanding the module system clarifies exactly why this second mechanism exists at all: it's the textual fallback for exactly the kind of non-modular header this section describes, needed only because your own project's headers usually aren't wrapped in a framework module the way a full framework's public API is.
A minimal framework module map
umbrella header names the single header expected to #import every other public header the module should expose. export * at the top level re-exports everything reachable from the umbrella header as the module's own public interface. module * { export * } additionally re-exports anything the umbrella header itself imports from other modules, so a client doesn't have to separately @import a dependency your framework already depends on internally.
framework module Networking {
umbrella header "Networking.h"
export *
module * {
export *
}
}Code Examples
Both lines make Foundation's API available, and with 'Enable Modules' on (the default for modern targets), the #import line is silently upgraded by the compiler to a module load rather than a genuine text paste — so in practice they behave nearly identically today. The distinction matters conceptually: #import's meaning depends on a build setting being enabled, while @import always and unconditionally means 'load this module,' with no textual-paste fallback possible. Expected result: ( Ada, Grace, Katherine )
@import Foundation; — unlike #import, this can never fall back to textual pasting; it is module syntax, full stop.
The module map declares Networking as a framework module whose entire public surface is defined by what Networking.h transitively imports. APIClient.h and RequestBuilder.h are public headers folded into the umbrella, so @import Networking; exposes their declarations. InternalRetryPolicy.h, marked as a private header in the target's Headers build phase, is never referenced from the umbrella, so it falls outside the module's boundary entirely — ordinary client code has no module-level way to reach it, even though the compiled framework binary still contains its implementation.
// InternalRetryPolicy.h is deliberately NOT imported here — this single omission is what keeps it out of the module's public interface.
This illustrates the practical payoff of the semantic-versus-textual distinction covered earlier in the lesson. A macro defined in your file before a textual #import could, under the old model, alter how the subsequently pasted header text was interpreted. Because a module is compiled once, independently, into a fixed semantic form before your file ever imports it, your local macros have no mechanism to reach into and change that already-finished representation — the module's meaning is locked in at module-build time, not at your file's import time.
The module was already parsed and precompiled before your #define existed in this translation unit's preprocessing state — there is no path for your macro to leak into it.
This is a shell-level narration rather than Objective-C code, showing the mechanism described in the 'module cache' section concretely: the cache key is a hash over the compiler flags that affect header semantics, not just the header text itself. Changing the deployment target changes that hash, so the previously cached module no longer applies and Clang must rebuild it — which is exactly the kind of surprising 'why is this suddenly rebuilding everything' moment that module-cache mechanics explain.
Cache miss: Foundation's module ... is recompiled fresh for the new configuration. — the .pcm is keyed by flags, not just by which module it represents.
// Traditional, textual-looking form — still compiles today
#import <Foundation/Foundation.h>
// Explicit module form — unambiguous, always a module load, never a text paste
@import Foundation;
NSArray<NSString *> *names = @[@"Ada", @"Grace", @"Katherine"];
NSLog(@"%@", names);framework module Networking {
umbrella header "Networking.h"
export *
module * {
export *
}
}
// Networking.h (the umbrella header)
#import <Networking/APIClient.h>
#import <Networking/RequestBuilder.h>
// Note: Networking/InternalRetryPolicy.h is deliberately NOT imported here —
// it is a private header, invisible to the module's public interface.// In YourFile.m, BEFORE the #import:
#define MAX_RETRIES 3
#import <Networking/Networking.h>
// Under the textual-include model (pre-modules), if Networking.h's own
// headers happened to use a macro or identifier named MAX_RETRIES
// internally, YOUR #define could have silently clashed with it.
//
// Under the module model, Networking was already fully parsed and
// precompiled into its own .pcm BEFORE your file's #define MAX_RETRIES
// even existed at compile time for this translation unit — so your
// macro cannot reach backward into Networking's already-finished
// semantic representation at all.
NSLog(@"Retries configured: %d", MAX_RETRIES);# First build, deployment target 13.0:
$ xcodebuild -scheme Networking IPHONEOS_DEPLOYMENT_TARGET=13.0
# Clang compiles Foundation's module fresh, writes a .pcm keyed to
# (deployment target 13.0, arch arm64, active macros, ...), caches it
# under DerivedData/.../ModuleCache.noindex/
# Second build, same session, same flags:
$ xcodebuild -scheme Networking IPHONEOS_DEPLOYMENT_TARGET=13.0
# Cache hit: Foundation's .pcm is loaded directly, no header reparse.
# Third build, deployment target changed:
$ xcodebuild -scheme Networking IPHONEOS_DEPLOYMENT_TARGET=15.0
# Flag hash no longer matches the cached .pcm's key.
# Cache miss: Foundation's module (and anything depending on it)
# is recompiled fresh for the new configuration.From header to module to two import styles
Both import styles can end up loading the same cached .pcm, but only @import guarantees it — #import's behavior depends on whether modules are enabled for the build.
Framework headers
|
v
module.modulemap (umbrella header + export rules)
|
v
Clang compiles once --> precompiled module (.pcm) --> Module Cache (keyed by flags)
| |
v v
@import Framework; #import <Framework/Framework.h>
(always a module load) (module load IF 'Enable Modules' is on,
else falls back to textual paste)Important Rules
A Clang module is a precompiled, semantic representation of a header's API, built once and cached — not the same operation as pasting header text.
Textual #import can be silently upgraded to a module load by the compiler when modules are enabled; @import is always and unambiguously a module load.
A framework module's public interface is exactly what its umbrella header transitively imports and its module map exports — nothing more.
Private headers, deliberately excluded from the umbrella header, fall outside the module's boundary even though their compiled code still ships inside the framework binary.
The module cache stores compiled modules keyed by the compiler flags that affect their meaning, so a build-setting change (like a deployment target) can invalidate cached modules and trigger a larger-than-expected rebuild.
Swift imports Objective-C frameworks by loading their compiled modules, not by reparsing raw header text — this is a direct mechanical foundation of clean Swift/Objective-C interop.
Why this matters day to day
Most Objective-C engineers never hand-write a module map and rarely think about the module cache — until a build that used to take fifteen seconds suddenly takes five minutes after someone tweaks a deployment target or preprocessor flag, or until a Swift teammate asks why their Objective-C framework's helper class 'isn't visible from Swift' when it's clearly compiled and shipping in the binary. Both situations trace directly back to the mechanics in this lesson: the first to flag-keyed module-cache invalidation, and the second to a header that was never routed through the umbrella header and is therefore genuinely outside the module's exported interface, not a Swift bug. Recognizing these as module-system mechanics rather than mysterious tooling failures turns a confusing multi-hour investigation into a five-minute, well-understood fix.
Assuming deleting a private header from the umbrella header's #import list has no real effect since the class is still compiled into the framework.
Recognize that omitting a header from the umbrella header removes it from the module's public interface entirely — @import Framework; will no longer expose that header's declarations to any client, even though the compiled implementation still exists inside the binary.
A module's boundary is defined by what the umbrella header (and module map export rules) reach, not by what the framework binary happens to contain. Treating 'still compiled in' as equivalent to 'still part of the module' misunderstands how modules enforce interface boundaries.
Blaming a sudden full rebuild after changing a build setting on 'Xcode being flaky' rather than investigating the module cache.
Recognize that the module cache keys compiled modules by compiler flags (including deployment target, macros, and architecture), so a build-setting change is expected to invalidate cached modules and trigger a rebuild of anything that depends on them.
This is normal, explainable module-cache behavior, not a tooling bug — understanding the flag-keyed cache avoids wasted time chasing a phantom 'flakiness' explanation for what is actually a legitimate cache-invalidation event.
What fundamentally distinguishes a Clang module from a textual #include/#import? A. A module can only contain one header, while #include can span many B. A module is a precompiled, semantic representation of an API built once and cached, whereas textual inclusion re-pastes and re-parses header text in every translation unit C. Modules are exclusive to Swift and have no relevance to Objective-C D. There is no real difference; @import is purely a stylistic alias for #import
B. A module is a precompiled, semantic representation of an API built once and cached, whereas textual inclusion re-pastes and re-parses header text in every translation unit — A Clang module serializes the parsed, typechecked semantic result of a header's declarations into a precompiled form (a .pcm) that can be loaded directly by later translation units, avoiding both repeated parsing and the macro-leakage risks inherent in textual pasting.
In a framework's module.modulemap, what determines the module's public interface? A. Every header file physically present anywhere in the framework bundle, public or private B. Only the headers the umbrella header transitively imports, combined with the map's export rules C. Whatever headers were most recently modified D. The framework's Info.plist file
B. Only the headers the umbrella header transitively imports, combined with the map's export rules — A module's boundary is exactly what the umbrella header (and any explicit submodule declarations with their own export rules) makes reachable — headers never routed through the umbrella, such as private/project headers, fall outside the module even though they're compiled into the framework binary.
Why can a preprocessor macro defined in your own .m file not corrupt the meaning of a framework's headers once that framework is imported as a module? A. Because Objective-C disallows #define entirely B. Because the module was already fully parsed and precompiled into a fixed semantic form before your file's macros existed at compile time for that translation unit C. Because modules automatically rename every macro to avoid collisions D. Because macros can only be defined inside module maps
B. Because the module was already fully parsed and precompiled into a fixed semantic form before your file's macros existed at compile time for that translation unit — A module is compiled once into a locked-in semantic representation, independent of any particular importer's preprocessor state. Since your macro is defined at your translation unit's preprocessing time — after the module's own build already finished — there's no textual-paste path left for it to interfere with the module's interpretation.
What is the module cache keyed by, and why does changing a deployment target sometimes trigger a large rebuild? A. It's keyed only by file modification timestamps, and rebuilds happen whenever any file anywhere changes B. It's keyed by a hash of the compiler flags affecting header semantics (including deployment target), so a flag change invalidates cached modules built under the old flag set C. It's keyed alphabetically by module name and never invalidates automatically D. Deployment target changes never affect the module cache
B. It's keyed by a hash of the compiler flags affecting header semantics (including deployment target), so a flag change invalidates cached modules built under the old flag set — Because a header's meaning can depend on flags like the deployment target, active macros, and target architecture, Clang keys each cached .pcm by a hash of those flags. Changing a relevant flag produces a different hash, causing a cache miss and forcing the affected modules to be rebuilt from scratch.
How does the module system directly support clean Swift-to-Objective-C interoperability? A. Swift re-parses each Objective-C header's raw text independently every time it imports a framework B. Swift loads a framework's already-compiled Clang module (the same precompiled semantic representation used by Objective-C) and translates its declarations into an idiomatic Swift interface C. Swift and Objective-C share no common representation and interop relies purely on the bridging header for every framework D. Modules are irrelevant to Swift; Swift only ever uses bridging headers
B. Swift loads a framework's already-compiled Clang module (the same precompiled semantic representation used by Objective-C) and translates its declarations into an idiomatic Swift interface — When Swift code writes import SomeFramework, it loads SomeFramework's compiled Clang module — the same precompiled, typechecked semantic representation described throughout this lesson — and runs it through the Objective-C import machinery to produce a native Swift API surface, rather than re-deriving that structure from raw header text.
What is the practical difference between @import Framework; and a plain #import <Framework/Framework.h>? A. There is no difference under any circumstances B. @import unconditionally performs a module load, while #import's behavior depends on whether modules are enabled for the build — it may be upgraded to a module load or may fall back to a textual paste C. #import always performs a module load and @import always performs a textual paste D. @import can only be used from Swift files, never from Objective-C files
B. @import unconditionally performs a module load, while #import's behavior depends on whether modules are enabled for the build — it may be upgraded to a module load or may fall back to a textual paste — @import is explicit, unambiguous module syntax with no textual-paste fallback possible. Plain #import remains a textual operation in principle, but with 'Enable Modules' on (the modern default), the compiler transparently substitutes a module load for it when the imported framework is available as a module.
You maintain a framework called ImageKit with public headers Filter.h and Renderer.h, and a private header GPUInternals.h used only inside the framework's own implementation. Write the module.modulemap and the umbrella header ImageKit.h so that @import ImageKit; exposes Filter and Renderer but never exposes GPUInternals, and explain in one sentence why GPUInternals stays hidden even though it is compiled into the framework binary.
module.modulemap: framework module ImageKit { umbrella header "ImageKit.h" export * module * { export * } } ImageKit.h: #import <ImageKit/Filter.h> #import <ImageKit/Renderer.h> // GPUInternals.h intentionally omitted GPUInternals stays hidden because the module's public interface is defined entirely by what the umbrella header transitively imports plus the map's export rules — since GPUInternals.h is never referenced from ImageKit.h, it falls outside that boundary regardless of the fact that its compiled code still ships inside the ImageKit binary.
The umbrella header's contents are what define the module's boundary — only #import what you actually want exported from it.