Widget archival failed due to image being too large

I'm trying to setup a widget to pull an image down from a webserver and I'm running into an error of Widget archival failed due to image being too large [9] - (1024, 1024), totalArea: 1048576 > max[718080.000000].

I've tried two different approaches to resolve this error and both have failed to resolve the image.

I've also confirmed that I'm getting the image in the AppIntentTimelineProvider.

    private func getImageUI(urlString: String) -> UIImage? {
        guard let url = URL(string: urlString) else { return nil }
        guard let imageData = try? Data(contentsOf: url) else { return nil }

        return UIImage(data: imageData)?.resizedForWidget()
    }

Is there another approach I could take on addressing this issue so the image appears on the widget?

Simple approach

extension UIImage {
  func resized(toWidth width: CGFloat, isOpaque: Bool = true) -> UIImage? {
    let canvas = CGSize(width: width, height: CGFloat(ceil(width/size.width * size.height)))
    let format = imageRendererFormat
    format.opaque = isOpaque
    return UIGraphicsImageRenderer(size: canvas, format: format).image {
      _ in draw(in: CGRect(origin: .zero, size: canvas))
    }
  }
}
extension UIImage {
    /// Resize the image to strictly fit within WidgetKit’s max allowed pixel area (718,080 pixels)
    func resizedForWidget(maxArea: CGFloat = 718_080.0, isOpaque: Bool = true) -> UIImage? {
        let originalWidth = size.width
        let originalHeight = size.height
        let originalArea = originalWidth * originalHeight

        print("🔍 Original Image Size: \(originalWidth)x\(originalHeight) → Total Pixels: \(originalArea)")

        // ✅ If the image is already within the limit, return as is
        if originalArea <= maxArea {
            print("✅ Image is already within the allowed area.")
            return self
        }

        // 🔄 Calculate the exact scale factor to fit within maxArea
        let scaleFactor = sqrt(maxArea / originalArea)
        let newWidth = floor(originalWidth * scaleFactor) // Use `floor` to ensure area is always within limits
        let newHeight = floor(originalHeight * scaleFactor)
        let newSize = CGSize(width: newWidth, height: newHeight)

        print("🛠 Resizing Image: \(originalWidth)x\(originalHeight) → \(newWidth)x\(newHeight)")

        // ✅ Force bitmap rendering to ensure the resized image is properly stored
        let format = UIGraphicsImageRendererFormat()
        format.opaque = isOpaque
        format.scale = 1  // Ensures we are not letting UIKit auto-scale it back up

        let renderer = UIGraphicsImageRenderer(size: newSize, format: format)
        let resizedImage = renderer.image { _ in
            self.draw(in: CGRect(origin: .zero, size: newSize))
        }

        print("✅ Final Resized Image Size: \(resizedImage.size), Total Pixels: \(resizedImage.size.width * resizedImage.size.height)")
        return resizedImage
    }
}

These are logs from a failed image render if that helps

  • 🔍 Original Image Size: 720.0x1280.0 → Total Pixels: 921600.0
  • 🛠 Resizing Image: 720.0x1280.0 → 635.0x1129.0
  • ✅ Final Resized Image Size: (635.0, 1129.0), Total Pixels: 716915.0

Hi,

For WidgetKit there are a few things to consider:

You get 10MB of space per timeline. You get 72 refreshes per day and an unlimited amount of entries. This means you can create a timeline of size 10MB and refresh every 20 minutes.

The system, or Springboard essentially, will reload if resources are available to do such. None of this is guaranteed. OnAppear isn't really valid for Widgets because they don't explicitly have a lifecycle and UI should be driven by your entry values, not runtime updates when the UI appears. The views themselves are archived and saved as a timeline so even in onAppear things aren't editable.

Lastly, if you think there is an issue with reloading, the best first course of action would be to use Console.app to monitor to the relevant processes related to your app and extensions.

A good way to start would be with filtering by the BundleID of the target and / or the target name itself (the process name). To debug WidgetKit, LiveActivities, Dynamic Island you'll want to monitor springboardd, liveactivitiesd along with other relevant processes.

The logs should tell you if your timeline is too large. If so, you can adjust your image sizes.

Hopefully this helps.

Rico


WWDR | DTS | Software Engineer

Widget archival failed due to image being too large
 
 
Q