# Share files between your iOS app, Widget and WatchKit extensions

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.

![Boundaries](https://cdn.hashnode.com/res/hashnode/image/upload/v1667764791840/RFpoIkL8e.jpeg align="left")

# File sharing between iOS app and Widget extension

iOS apps are [sandboxed](https://support.apple.com/guide/security/security-of-runtime-process-sec15bfe098e/web) to prevent apps from gathering or modifying information stored by other apps.  This includes [app extensions](https://developer.apple.com/app-extensions/) like widgets.

Your iOS app can grant app extensions and other apps access to files by declaring an [App Groups entitlement](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups). Apps within an app group share access to a group container.

![App Group Entitlement](https://cdn.hashnode.com/res/hashnode/image/upload/v1667748256779/J_qTdMJ3J.gif align="left")

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

![Create new app group](https://cdn.hashnode.com/res/hashnode/image/upload/v1667747367247/xEdYo8s9F.png align="left")

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.

## Saving a file in your iOS app

```swift
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)
```

## Reading the file from your Widget extension

```swift
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\"}"
```

# File sharing between iOS app and WatchKit extension

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`](https://developer.apple.com/documentation/watchconnectivity) 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. 

## Transfer the file from your iOS app to your WatchKit extension

```swift
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.

```swift
// Prerequisite 
if WCSession.isSupported() {
  WCSession.default.activate()
}
```

## Store the file in your WatchKit extension

Use [`session(_:didReceive:)`](https://developer.apple.com/documentation/watchconnectivity/wcsessiondelegate/1615651-session)

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.

```swift
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)
  }
}
```
