Why some Swift types only appear when you import two modules

⋅ 4 min read ⋅ Swift SwiftUI Xcode

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.

Sponsor sarunw.com and reach thousands of iOS developers.

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.swiftcrossimport holds both a SwiftUI.swiftoverlay and a UIKit.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.

Sponsor sarunw.com and reach thousands of iOS developers.

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 Share
Previous
SwiftUI 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.

← Home