Supported way to use MapKit in a sandboxed macOS Quick Look extension?

I’m developing a sandboxed macOS app with a Quick Look extension that previews user-selected files.

The preview includes an interactive MapKit map showing the route from the file

During TestFlight review, Apple rejected temporary entitlement exceptions for:

com.apple.security.temporary-exception.files.home-relative-path.read-only /Library/Caches/GeoServices/

com.apple.security.temporary-exception.mach-lookup.global-name com.apple.geoanalyticsd

I understand these temporary exceptions are not appropriate for Mac App Store distribution and will remove them.

What is the supported sandbox-compliant way to use MapKit inside a macOS Quick Look extension?

Should an interactive MapKit view work inside a sandboxed Quick Look extension without temporary exceptions, or is MapKit unsupported in this extension context?

If interactive MapKit is not supported, is MKMapSnapshotter the recommended alternative, or should the extension render a route-only preview without Apple map tiles?

Any guidance on the expected entitlement/capability setup for this scenario would be appreciated.

Answered by DTS Engineer in 894362022

Hi @n0rt0nthec4t,

You wrote:

[...] This is the rendering I get with either MKMapSnapshotter OR MKMapView [...]

The behavior is two-fold—QuickLook extensions are sandboxed and prevent network access to protect the user's information. However, MapKit is a system framework and this use case should be reconsidered.

I'd suggest for you to create a report via Feedback Assistant. Once submitted, please reply here with the Feedback ID so I can escalate with the Quick Look team directly.

I want to be clear, this is not a bug or oversight, but a deliberate security boundary, there is no guarantee this will be fixed in a later release. MapKit's tile fetching is network activity. Even though the intent is to download map imagery (not upload user data), the sandbox doesn't distinguish between inbound and outbound purposes at the Mach service level.

Note: Even if other developers report this issue, it is essential that you each submit your own bug report. The number of bug reports for this issue will improve the likelihood of the underlying issue to be resolved quickly.

As a workaround, you could generate a static map image in your main app (which has full MapKit access) and cache it alongside or embedded in the file metadata:

// In your main app (full sandbox profile, MapKit works)
let snapshotter = MKMapSnapshotter(options: options)
snapshotter.start { snapshot, error in
    guard let snapshot = snapshot else { return }
    let image = snapshot.image
    // Draw your route overlay onto the snapshot
    // Cache the composed image (e.g., in extended attributes, 
    // a sidecar file, or embedded in your document format)
}

Then, in your Quick Look extension, simply load and display the cached image:

// In your QLPreviewingController
func providePreview(for request: QLFilePreviewRequest) async throws -> QLPreviewReply {
    let reply = QLPreviewReply(contextSize: size, isBitmap: true) { context,
        reply in
        // Load pre-rendered map image from the file/sidecar
        let mapImage = self.loadCachedMapImage(for: request.fileURL)
        mapImage?.draw(in: rect)
        return true
    }
    return reply
}

Alternatively, if you only need to show the route shape (not full cartography), draw the polyline yourself in Core Graphics:

func providePreview(for request: QLFilePreviewRequest) async throws -> QLPreviewReply {
    return QLPreviewReply(contextSize: size, isBitmap: false) { context, reply in
        let path = CGMutablePath()
        // Convert route coordinates to view points via a simple 
        // Mercator projection fitted to the bounding box
        for (i, point) in projectedPoints.enumerated() {
            if i == 0 { path.move(to: point) }
            else { path.addLine(to: point) }
        }
        context.setStrokeColor(NSColor.systemBlue.cgColor)
        context.setLineWidth(3.0)
        context.addPath(path)
        context.strokePath()
        return true
    }
}

Lastly, consider using a mix of the pre-rendered base map as a fallback (option 1), overlaying live route geometry (option 2) from the file data. This keeps the preview responsive to file changes for the route while using a periodically-refreshed background.

Cheers,

Paris X Pinkney |  WWDR | DTS Engineer

This is the rendering I get with either MKMapSnapshotter OR MKMapView

Tahoe 26.5.1

Entitlements:

<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.developer.maps</key> <true/> <key>com.apple.security.app-sandbox</key> <true/> <key>com.apple.security.application-groups</key> <array> <string>group.dev.n0rt0nthec4t.trailbridge</string> </array> <key>com.apple.security.network.client</key> <true/> </dict> </plist>

Hi @n0rt0nthec4t,

You wrote:

[...] This is the rendering I get with either MKMapSnapshotter OR MKMapView [...]

The behavior is two-fold—QuickLook extensions are sandboxed and prevent network access to protect the user's information. However, MapKit is a system framework and this use case should be reconsidered.

I'd suggest for you to create a report via Feedback Assistant. Once submitted, please reply here with the Feedback ID so I can escalate with the Quick Look team directly.

I want to be clear, this is not a bug or oversight, but a deliberate security boundary, there is no guarantee this will be fixed in a later release. MapKit's tile fetching is network activity. Even though the intent is to download map imagery (not upload user data), the sandbox doesn't distinguish between inbound and outbound purposes at the Mach service level.

Note: Even if other developers report this issue, it is essential that you each submit your own bug report. The number of bug reports for this issue will improve the likelihood of the underlying issue to be resolved quickly.

As a workaround, you could generate a static map image in your main app (which has full MapKit access) and cache it alongside or embedded in the file metadata:

// In your main app (full sandbox profile, MapKit works)
let snapshotter = MKMapSnapshotter(options: options)
snapshotter.start { snapshot, error in
    guard let snapshot = snapshot else { return }
    let image = snapshot.image
    // Draw your route overlay onto the snapshot
    // Cache the composed image (e.g., in extended attributes, 
    // a sidecar file, or embedded in your document format)
}

Then, in your Quick Look extension, simply load and display the cached image:

// In your QLPreviewingController
func providePreview(for request: QLFilePreviewRequest) async throws -> QLPreviewReply {
    let reply = QLPreviewReply(contextSize: size, isBitmap: true) { context,
        reply in
        // Load pre-rendered map image from the file/sidecar
        let mapImage = self.loadCachedMapImage(for: request.fileURL)
        mapImage?.draw(in: rect)
        return true
    }
    return reply
}

Alternatively, if you only need to show the route shape (not full cartography), draw the polyline yourself in Core Graphics:

func providePreview(for request: QLFilePreviewRequest) async throws -> QLPreviewReply {
    return QLPreviewReply(contextSize: size, isBitmap: false) { context, reply in
        let path = CGMutablePath()
        // Convert route coordinates to view points via a simple 
        // Mercator projection fitted to the bounding box
        for (i, point) in projectedPoints.enumerated() {
            if i == 0 { path.move(to: point) }
            else { path.addLine(to: point) }
        }
        context.setStrokeColor(NSColor.systemBlue.cgColor)
        context.setLineWidth(3.0)
        context.addPath(path)
        context.strokePath()
        return true
    }
}

Lastly, consider using a mix of the pre-rendered base map as a fallback (option 1), overlaying live route geometry (option 2) from the file data. This keeps the preview responsive to file changes for the route while using a periodically-refreshed background.

Cheers,

Paris X Pinkney |  WWDR | DTS Engineer

@DTS Engineer FB23253481

Hi @n0rt0nthec4t,

Thank you for your Feedback ID. I've passed this along to the MapKit and QuickLook engineering teams for further investigation.

Cheers,

Paris X Pinkney |  WWDR | DTS Engineer

@DTS Engineer

I think your explanation is not entirely accurate. Actually the sandbox is already treating different system components differently. Therefore it seems very unlikely that the sandbox can not distinguish between "allowed" and "forbidden" network traffic.

MapKit is an encapsulated system component, so it is more or less impossible for a Quicklook extension to send random and uncontrolled network requests. All the network access is completely controlled by the system. So it seems extremely unlikely that there are serious security issues here. BTW: in the past, MapKit did work in Quicklook extensions.

On the other hand, WebKit (WKWebView) is(!) working in Quicklook extensions just fine. But unlike MapKit, WebKit is able to do random and uncontrolled network requests, therefore could be a security issue.

Which means the sandbox is actually distinguishing between different system components. The harmless one it does block, the "dangerous" one it let pass.

I do understand that WebKit is "allowed". There are many extensions which need network access (including many of Apple's own), for example to display web link previews, video/audio streams. So it makes much sense that this is possible.

My thoughts:

Instead of making the macOS less useful by blocking all kinds of useful Quicklook extensions, maybe it would be better to simply ask the user for permission, like this is done for other security or privacy related things (like accessing photos, calendar, location services etc). So the user can decide.

And in practice, the user is already forced to enable each Quicklook extension manually in the settings. So it is already impossible that an extension can be used without the knowledge of the user.

Honestly, what is the point in blocking network access of a Quicklook extension, if the Host-App of this extension still has full network access. If an App would do something bad, it does not need its Quicklook extension, it can do this itself and much more. Also because Quicklook extensions are now always part of their "Host" App, it is impossible to have any invisible hidden extensions lying around, which the suer can forget about. If the App is deleted, the extension is gone automatically as well. So if the macOS tries the Quicklook extension as "dangerous" it would also have to treat the host App as dangerous and would have to apply all restrictions to both. But of course, this would make the whole system completely unusable. In the end we must give the user the opportunity to decide. Does the user trust an App to use it, then this should apply to the Apps extensions as well. For and possible danger it is fine to ask the user for permission, but if the user allows anything, the system doesn't need to block this anyways.

And at the end Apple can still reject a developer certificate to "kill" the App in case it turns out that the App or developer is doing something bad.

WebKit (WKWebView) is(!) working in Quicklook extensions just fine. But unlike MapKit, WebKit is able to do random and uncontrolled network requests, therefore could be a security issue.

Wouldn't that be a solution then? You can display your map data in a web view.

the user is already forced to enable each Quicklook extension manually in the settings.

There's a notification but I think Quicklook extensions are allowed by default. Users can turn them off if they want.

Honestly, what is the point in blocking network access of a Quicklook extension, if the Host-App of this extension still has full network access.

Perhaps it isn't a security issue at all. Perhaps the fact that it's a system component is the key issue. MapKit is a data service. Those bytes are billed to your account. But if the system is making the call, then your app gets free map tiles.

So, if you use a web view, then you can use your API key and have the data properly accounted for.

@DTS Engineer This exact issue was actually reported almost 2 years ago, when MapKit support in Quick Look suddenly stopped working with the release of macOS Sequoia.

You've said:

this use case should be reconsidered.

I want to be clear, this is not a bug or oversight, but a deliberate security boundary,

However, MapKit in Quick Look was working fine all along (I've started implementing this around the time of macOS 10.15 / macOS 11), until Sequoia came along. It was a supported use case. It is a bug.

I've initially filed it via a DTS TSI with Case ID: 9543658. Please attach FB15634383 (https://developer.apple.com/forums/thread/766615) along with this.

Very unfortunate that this has dragged on for years without a fix.

Don't use the comments feature in the forums. Your replies can get missed that way.

Ironically, this solution would become a security concern for the end-user as their file data would be sent to some random website for display, unlike the local solution where data is overlayed on top of MapKit.

There is no local solution. All map data is served from the web. MapKit is also available on the web via MapKit JS.

MapKit is free to use in macOS/iOS apps.

Yes and no. There is currently no charge for displaying map tiles in a map view. There are usage limits, but those limits aren't documented. Some MapKit APIs, including MapKit JS, do have documented limits.

Because the system has limits, there must be accounting. And if there's accounting, the developer has to account for what happens when those limits are reached.

I don't actually know that native MapKit tracks usage. It seems like a logical assumption, but you never know.

Also, it seems as if MapKit is communicating via some local XPC service. MapKit runs just fine in an app that doesn't have internet capability at all. If this is true, then there's absolutely no reason why QuickLook shouldn't run as the connection is merely a local XPC. But again, that's just speculation on my part. There could be all kinds of system complexities that I don't know about.

@Etresoft I think you misunderstood what I meant by local solution: I mean stuff like annotations, polylines, polygons (which in would be the user's data) are rendered locally on the map view.

I think you misunderstood what I meant by local solution: I mean stuff like annotations, polylines, polygons (which in would be the user's data) are rendered locally on the map view.

Most JS-based mapping frameworks can display local data. In most cases, that's the only way to display local data as mapping services typically only serve tiles.

There are services like Google Maps and ArcGIS where you could upload local data for display on a map, but that would only be required for other people to see the (no longer) local data.

However, I don't know if MapKit JS can support this. Maybe it does, I've never looked into it. MapKit has poor performance and extremely restrictive licensing so I've never really considered it.

There must be dozens of these kinds of JS map frameworks to choose from. If using WebKit really does allow you to bypass the network block in QuickLook, that really is a great solution. You might be surprised by how well it works.

Supported way to use MapKit in a sandboxed macOS Quick Look extension?
 
 
Q