As far as I can tell, the Live Activity itself is not capable of performing networking tasks and, thus, AsyncImage
is not likely to be suitable. As the Live Activity is more aligned with how a Widget
works, taking the approach of how images are shared between a host/parent app and the Widget Extension seems to be a viable way to display images on the Lock Screen/Dynamic Island within ActivityKit
.
Data can be shared between a host/parent app and a Widget Extension by taking advantage of App Groups (you can find this under the Signing & Capabilities tab of your project's settings in Xcode). In your host/parent target, you can enable App Groups, and choose either one of your existing App Groups (or create a new App Group). You can do the same for your Widget Extension target, as well, hereby allowing your host/parent app and the Widget Extension to have a shared App Group repository that can share data.
Then, in your host/parent app, you can add in or modify an existing method that pre-fetches an image. For example, something like;
private func downloadImage(from url: URL) async throws -> URL? {
guard var destination = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.example.liveactivityappgroup")
else { return nil }
destination = destination.appendingPathComponent(url.lastPathComponent)
guard !FileManager.default.fileExists(atPath: destination.path()) else {
print("No need to download \(url.lastPathComponent) as it already exists.")
return destination
}
let (source, _) = try await URLSession.shared.download(from: url)
try FileManager.default.moveItem(at: source, to: destination)
print("Done downloading \(url.lastPathComponent)!")
return destination
}
In my example here, I am taking in a URL
, say of an image on the internet, checking if my "App Group" container exists for this target, appending the file name to the "App Group" container's FileManager
URL
, and either returning that URL
, if the image already has been downloaded, or downloading the image.
Once you have your image downloaded and stored in the "App Group" container, your Widget Extension/Live Activity could take advantage of this. For example, in your ActivityAttributes
(or ActivityAttributes.ContentState
) that you have defined for your Live Activity, you could have a var imageName: String
that gets set when you create/update your Live Activity. Since this would then be passed via the context
to the Widget Extension, you could do the reverse in your Widget Extension/Live Activity and render the image.
For example, in your Widget Extension/Live Activity's code, you could add something like;
if let imageContainer = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.example.liveactivityappgroup")?
.appendingPathComponent(context.state.imageName),
let uiImage = UIImage(contentsOfFile: imageContainer.path()) {
Image(uiImage: uiImage)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 50, height: 50)
}
This checks that the Widget Extension can access the App Group repository, appends the image name, as passed in the context
, creates a UIImage
, and if successful, then renders the UIImage
using SwiftUI's Image
.