Why some Swift types only appear when you import two modules
Table of Contents
Here is a piece of SwiftUI that does not compile.
import SwiftUI
struct ContentView: View {
var body: some View {
Map() // error: cannot find 'Map' in scope
}
}
Map is a SwiftUI view. It has a View conformance, it takes SwiftUI modifiers, and the documentation files it under SwiftUI. But SwiftUI does not define it.
Add one import and it builds.
import SwiftUI
import MapKit
struct ContentView: View {
var body: some View {
Map()
}
}
Map lives in a cross-import overlay: a module that only exists when two other modules are both imported.
What is actually happening
When we import MapKit and SwiftUI in the same file, the compiler quietly loads a third module named _MapKit_SwiftUI. That module holds Map and everything else that needs to know about both frameworks at once.
We never write that name. The underscore says so. The compiler finds it, loads it, and the types simply appear.
Miss one of the two imports and the module is never loaded, so the type is not in scope. That is why the error says "cannot find 'Map' in scope" rather than something about a missing import. As far as the compiler is concerned, in that file, Map does not exist.
You can easily support sarunw.com by checking out this sponsor.
AI Paraphrase:Are you tired of staring at your screen, struggling to rephrase sentences, or trying to find the perfect words for your text?
Why Apple does this
Map needs both frameworks. It is a View, so it needs SwiftUI. It shows a map and takes a MKCoordinateRegion, so it needs MapKit.
Whichever framework it lived in would have to depend on the other. Put Map in SwiftUI and every app that imports SwiftUI drags in MapKit, including apps that never show a map. Put it in MapKit and the reverse happens.
A cross-import overlay avoids the choice. The extra code sits in its own module that loads only when a file has already asked for both, so nobody pays for a dependency they are not using.
How to spot one
Each framework declares its own overlays inside its bundle in the SDK. MapKit's looks like this:
MapKit.framework/
└── Modules/
└── MapKit.swiftcrossimport/
└── SwiftUI.swiftoverlay
Two names carry all the meaning:
- The directory,
MapKit.swiftcrossimport, says MapKit has cross-import overlays at all. A framework with none has no such directory. - The file,
SwiftUI.swiftoverlay, is named after the other module. So this file is the rule for "MapKit together with SwiftUI". A framework that pairs with two modules has two files here:PermissionKit.swiftcrossimportholds both aSwiftUI.swiftoverlayand aUIKit.swiftoverlay.
SwiftUI.swiftoverlay is a few lines of YAML, and all it does is name the module to load:
---
version: 1
modules:
- name: _MapKit_SwiftUI
Put the two together and the rule reads: when a file imports MapKit and SwiftUI, also load _MapKit_SwiftUI.
To read it yourself:
SDK=$(xcrun --sdk iphonesimulator --show-sdk-path)
cd "$SDK/System/Library/Frameworks/MapKit.framework/Modules"
ls MapKit.swiftcrossimport
cat MapKit.swiftcrossimport/SwiftUI.swiftoverlay
The ones you are most likely to hit
The iOS 26 SDK ships 36 of these. Most pair a framework with SwiftUI, and these are the ones that catch people:
| Import alongside SwiftUI | To get |
|---|---|
| MapKit | Map, Marker, Annotation |
| PhotosUI | PhotosPicker |
| WebKit | WebView |
| StoreKit | SubscriptionStoreView, ProductView |
| QuickLook | .quickLookPreview(_:) |
| AVKit | VideoPlayer |
| SceneKit, SpriteKit, RealityKit | SceneView, SpriteView, RealityView |
| Translation | .translationPresentation |
| SwiftData | .modelContainer |
| AuthenticationServices | SignInWithAppleButton |
Not every overlay involves SwiftUI. SwiftData plus CoreData is one, CoreData plus CloudKit is another, and PhotosUI plus WidgetKit is a third.
The practical version
When a type that should obviously exist does not, and the error is "cannot find X in scope", the answer is usually a second import rather than a typo or a wrong deployment target.
The reliable trick is to look at which two frameworks the type touches. PhotosPicker is a picker for photos, so it needs SwiftUI and PhotosUI. WebView shows web content in a view, so it needs SwiftUI and WebKit. Import the pair and it appears.
Xcode's autocomplete has the same blind spot. It cannot suggest a type from a module it has not loaded, so the type will be missing from the completion list too, which makes it feel like the API is not there at all.
You can easily support sarunw.com by checking out this sponsor.
AI Paraphrase:Are you tired of staring at your screen, struggling to rephrase sentences, or trying to find the perfect words for your text?
Summary
A cross-import overlay is a hidden module that loads only when two other modules are both imported, and it is where Apple puts API that needs both. That is why Map needs MapKit, PhotosPicker needs PhotosUI, and WebView needs WebKit, on top of SwiftUI. When a type you know exists is not in scope, add the second import before looking for a deeper problem.
Read more article about Swift, SwiftUI, Xcode, or see all available topic
Enjoy the read?
If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.
Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.
If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.
Become a patron Buy me a coffee Tweet ShareSwiftUI has a native WebView in iOS 26
iOS 26 adds a real WebView to SwiftUI, plus a WebPage class that gives you the title, the loading progress, and control over navigation. No more UIViewRepresentable.