skip to Main Content

I use my app since a 10 years, and recently I realised that the app consumes a lot of data (in GigaBytes within a few days) and it actually does nothing… only sync a few simple records with iCloud database. So I have tried to debug where the app consumes the data, and I simplified everything. Now my app is just simple brown controller:

import Alamofire
import CoreData
import Foundation
import UIKit
import SwiftSoup
import StoreKit
import WebKit
import Firebase
import SVProgressHUD

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {

    var window: UIWindow?
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        
        let window = UIWindow()
        window.rootViewController = UIViewController()
        window.rootViewController?.view.backgroundColor = .brown
        window.backgroundColor = .white
        self.window = window
        self.window?.makeKeyAndVisible()
        return true
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        print("active")
    }
}

And that is all. I commented out everything for debug time. And that is most simplified code as possible. And the app still consumes data in MegaBytes every time when application did become active (active is printed). Why? Where else I can search for the source of that issue?
How do I know it?
I go to Settings > Mobile Data > My App – and it increases ~3-10 MB every time when app did Become active. Why?

enter image description here

UPDATE:

After a big research and long debugging I have discovered that for high data consumption are responsible widgets of my app.
I have 5 widgets, and each of them display images from url the following way:

extension Image {
    init(url: URL?) {
        if let url = url, let imageData = try? Data(contentsOf: url), let uiImage = UIImage(data: imageData) {
            self.init(uiImage: uiImage)
        } else {
            self.init(systemName: "photo.fill")
        }
    }
}

It seem that each time when an app did become active every widget is refreshed. And in total there is 20-30 images displayed from url.

When I remove the widgets from home screen, or remove widgets from embedded content the issue does not exist anymore.

Is there a way to cache it or improve performance?

2

Answers


  1. Chosen as BEST ANSWER

    So the simple answer to fix following issue was to add Cache-like tool for images based on url and delivered key:

    extension Image {
        init(url: URL?, key: String) {
            let defaults = UserDefaults.shared
            let urlKey = "(key)_url"
            let dataKey = "(key)_data"
            guard let url = url else {
                self.init(systemName: "photo.fill")
                return
            }
            
            if let cachedUrl = defaults.string(forKey: urlKey),
               let cachedData = defaults.data(forKey: dataKey),
               let image = UIImage(data: cachedData), cachedUrl == url.absoluteString {
                self.init(uiImage: image)
                return
            }
            
            if let imageData = try? Data(contentsOf: url), let uiImage = UIImage(data: imageData) {
                defaults.setValue(url.absoluteString, forKey: urlKey)
                defaults.setValue(imageData, forKey: dataKey)
                defaults.synchronize()
                self.init(uiImage: uiImage)
            } else {
                self.init(systemName: "photo.fill")
            }
        }
    }
    

    Example of usage:

    Image(url: URL(string: "{url}", key: "{key}"))
    

  2. There’s a reason this extension doesn’t exist on Image already. Image describes a view. The system is free to construct it many times, and it’s assumed to be cheap to create. Your code refetches the data synchronously (blocking the thread) every time init is called.

    The tool you want here is AsyncImage, which is designed for this use case.

    AsyncImage does not provide much caching itself (I believe it provides standard HTTP caching if your server handles that correctly), but it won’t re-load except when needed. If you want to add more extensive caching, see How can I add caching to AsyncImage.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search