Widget is crashing with 1: EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=30 MB, unused=0x0)

I have created widget which supports medium and large family similar to news feed app like title and image by configuring the medium widget with 2 articles and large with 4 articles where each article has title and it's image with size 46x46 and 61x61.

When am adding the small and large widgets to my Home Screen getting memory limit (Thread 1: EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=30 MB, unused=0x0)(crash and the widget is not refreshing and some times widgets is showing the skeletons instead of data.

This is my timeline provider     func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) {

            widgetVM.getRecommendedDataForWidget{ (result) in

                switch result {

                case .success(let items):

                    let entries =  getRecommendedWidgetTimeLineEntries(recFeedModel: items)

                    let timeline = Timeline(entries: entries, policy: .atEnd)

                    completion(timeline)

                case .failure(_):

                        let entry = RecommendedWidgetEntry(date: Date(), recommedationData: WidgetFeedModel.getPlaceholderData())

                        let timeline = Timeline(entries: [entry], policy: .never)

                        completion(timeline)

                }

            }

        }

        else {

            if accessTokenValue == nil { // if token is nil then considering it as member is not logged in.

                let entry = RecommendedWidgetEntry(date: Date(), recommedationData: [])

                let timeline = Timeline(entries: [entry], policy: .never)

                completion(timeline)

                return

            }

    }

Could you please help me out on this as this is priority for me as part of app release. I couldn't find out the reason for this.

Thanks

Accepted Reply

You should use URLSession to download the images in your timeline provider then pass the URLs to these files to your view. If you're still hitting the memory limit then resize them (for example using UIImage.preparingThumbnail(of:)) before displaying them in a view.

Don't use Data(contentsOf:) to download anything from the internet. You also don't need to wrap your views in AnyView.

Replies

The crash means you're going over the memory limit for widget extensions, which is currently 30MB as you can see from the error message. It's pretty easy to hit this limit, especially if you're working with images. Some things you can try are:

  • downloading the images directly to the file system and passing around their URLs
  • resizing large images to 46x46/61x61 before they're displayed

Am downloading the images like this and binding to widget UI. Correct me how can I proceed these memory limit issue.

struct NetworkImage: View {

    

    let url: URL?

    var viewDimension : CGFloat = 46

    

    var body: some View {

            if let url = url, let imageData = try? Data(contentsOf: url),

               let uiImage = UIImage(data: imageData) {

                AnyView( Image(uiImage: uiImage)

                    .resizable()

                    .aspectRatio(1, contentMode: .fit)

                    .frame(width: viewDimension, height: viewDimension, alignment: .center)

                    .border(Color(red: 0.957, green: 0.957, blue: 0.957), width: 2.0)

                    .cornerRadius(4)

                )

            }

            else {

                

                AnyView( EmptyView() )

            }

    }

}

  • My app allows users to take a photo and attach it to an item, so the photo itself can be huge. For Widgets and the Watch app I resize those images to whatever size is needed, then send those to the Widget/Watch. I never send the full-size image.

    It looks like you're sending a full-size image, then simply telling the view to resize it to 46px x 46px. Resize the image and save it as a new file before sending it to the extension.

Add a Comment

You should use URLSession to download the images in your timeline provider then pass the URLs to these files to your view. If you're still hitting the memory limit then resize them (for example using UIImage.preparingThumbnail(of:)) before displaying them in a view.

Don't use Data(contentsOf:) to download anything from the internet. You also don't need to wrap your views in AnyView.

Thanks for your reply. Am using UserDefaults to cache the images from timeline provider and it's working fine instead of using File System.

Again facing memory limit issue on my device after two days. Getting crashed when saving the image to file system.

Added the memory graph and object graph for the same.

Don't no how to move forward with remote images on my widgets.

Kindly suggest the way how to overcome this low memory issue.

What's the resolution of the image you're sending to the Widget?

Saving images from timeline provider into UserDefaults by compressing the quality and calling the getImageFromUserDefaults bind the image which was saved.

 //Get the images from the User Defaults based on resId.

    static func getImageFromUserDefaults(key: String) -> UIImage? {

        if let userDefaults = UserDefaults(suiteName: userDefaultsAppGroup) {

            if let imageData = userDefaults.object(forKey: key) as? Data,

               let image = UIImage(data: imageData) {

                return image

            }

        }

        

        return nil

    }

    

    //Save the images into User Defaults, reId as Key

    static func saveIntoUserDefaults(image : UIImage, resId: Int) {

        if let userDefaults = UserDefaults(suiteName: SharedContentConstants.userDefaultsAppGroup) {

            if let jpegRepresentation = image.jpegData(compressionQuality: 0.5) {

                    userDefaults.set(jpegRepresentation, forKey: String(resId))

                }

        }

    }

I'll ask again: What's the resolution of the image you're sending to the Widget?

If you're sending a massive image and letting the Widget scale it down, you're using far too much memory.

Imagine loading a 2000x2000px image and displaying it as a 10x10px image in a widget. It looks like that's what you're doing.

yes you are right. Am saving the compressed quality image into userdefaults and loading the image with actual resolution from user defaults and while displaying resizing the image based on widget like 61x61 and 46x46.

As per your suggestion now I am saving the image with 200x200 into user defaults and getting the same while displaying it as 46X46 for medium and 61x61 for large into widgets and. But still am getting memory limit exception while rendering the both medium( has two articles with images) and large family widgets (4 articles with images).

Working fine when rendering the data for medium widget ( with two articles and two images).

Note: Both the widgets has same data but no of records vary.

Any suggestions on this.