Share files between your iOS app, Widget and WatchKit extensions

I am a Software Engineer working on open source and enterprise mobile SDKs for iOS and MacOS developers written in Swift. From 🇩🇪 and happily living in 🇺🇸
Search for a command to run...

I am a Software Engineer working on open source and enterprise mobile SDKs for iOS and MacOS developers written in Swift. From 🇩🇪 and happily living in 🇺🇸
No comments yet. Be the first to comment.
WWDC26 kicked off on June 8, 2026. The information in this article reflects the information published by Apple on that date. There are 14 new frameworks. Name Description AccessoryAccess Manage

WWDC25 kicked off on June 9, 2025. The information in this article reflects the information published by Apple on that date. New Frameworks NameDescription AlarmKitSchedule prominent alarms and countdowns to help people manage their time. AVR...

WWDC25 is almost here, and I couldn’t be more excited! Whether you're attending the official events, community meetups, or just soaking in the energy around Cupertino, there’s no better time to connect, share ideas, and celebrate everything we love a...

In this blog post, I’ll share an observation and advice regarding the caching behavior of network responses by Apple’s APIs. If you are unfamiliar with caching of network responses then I recommend Apple’s article Accessing cached data that introduce...
WWDC24 kicked off on June 10, 2024. The information in this article reflects the information published by Apple on that date. New Frameworks NameDescription AccessorySetupKitUse AccessorySetupKit to discover accessories with Bluetooth or Wi-Fi...

In this blog post, you will learn how to share a file, downloaded or created by your iOS app, with your widget and/or WatchKit extension despite the sandbox or physical boundaries.

iOS apps are sandboxed to prevent apps from gathering or modifying information stored by other apps. This includes app extensions like widgets.
Your iOS app can grant app extensions and other apps access to files by declaring an App Groups entitlement. Apps within an app group share access to a group container.

In Xcode create a new, or select an existing, app group.

For iOS, the name format must be group.<group name>
In my examples, I will use a fictional group.us.eidinger.demo.SharedFiles group.
Off-topic: App groups also act as keychain access groups. Instead of sharing files, you can also share data in the keychain between your iOS app and your app extensions. This has the advantage that, if your users use iCloud Keychain, the data would also be accessible in your WatchKit extension. The examples below focus on sharing files as this does not force end-users to use iCloud Keychain.
let fileContent = "{\"Hello\":\"World\"}"
let sharedGroupContainerDirectory = FileManager().containerURL(
forSecurityApplicationGroupIdentifier: "group.us.eidinger.demo.SharedFiles")
guard let fileURL = sharedGroupContainerDirectory?.appendingPathComponent("sharedFile.json") else { return }
try? fileContent.data(using: .utf8)!.write(to: fileURL)
let sharedGroupContainerDirectory = FileManager().containerURL(
forSecurityApplicationGroupIdentifier: "group.us.eidinger.demo.SharedFiles")
guard let fileURL = sharedGroupContainerDirectory?.appendingPathComponent("sharedFile.json") else { return }
guard let fileContent = try? Data(contentsOf: fileURL) else { return }
print(String(data: fileContent, encoding: .utf8)) // "{\"Hello\":\"World\"}"
Your watch extension runs on a different physical device, your Apple Watch. Hence the file on your iPhone cannot be shared through App Groups!!
You must use Watch Connectivity to transfer data between your iOS app and the WatchKit extension of a paired watchOS app. You can pass small amounts of data or entire files.
Important: The Simulator app doesn’t support the necessary methods transferFile(_:metadata:) and session(_:didReceive:) methods and you must test file transfers on physical, paired devices.
WCSession.default.transferFile(fileURL, metadata: nil)
Important: This method can only be called while the session is active. Calling this method for an inactive or deactivated session is a programmer error.
// Prerequisite
if WCSession.isSupported() {
WCSession.default.activate()
}
You must move the file referenced by the file parameter if you intend to keep it. If you don’t move the file synchronously during implementing this delegate method, the system deletes the file when the method returns.
Example of implementing WCSessionDelegate.session(_:didReceive:) delegate method in your WatchKit extension.
class SessionDelegator: NSObject, WCSessionDelegate {
func session(
_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?
) {
// ignore for this test
}
func session(_ session: WCSession, didReceive file: WCSessionFile) {
guard let receivedFileContent = try? Data(contentsOf: file.fileURL) else {
return
}
let persistentFileURL = URL.documentsDirectory.appending(path: "sharedFile.json")
try? receivedFileContent.write(to: persistentFileURL)
}
}