0

I saved a UIImage to UserDefaults via .data with this code, where the key equals "petPhoto1":

@IBAction func addPhotoButton(_ sender: Any) {
        
        let picker = UIImagePickerController()
        picker.allowsEditing = false
        picker.delegate = self
        picker.mediaTypes = ["public.image"]
        present(picker, animated: true)
        
    }
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
        
        if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            image.storeInUserDefaults(for: "petPhoto\(activePet)")
            UserDefaults.standard.set("yes", forKey: "doesImageExist\(activePet)")
        }
        
        dismiss(animated: true, completion: nil)
    }

(unrelated stuff in between)

extension UIImage {
    func storeInUserDefaults(with compressionQuality: CGFloat = 0.8, for key:     String) {
    guard let data = self.jpegData(compressionQuality: compressionQuality) else { return }
    let encodedImage = try! PropertyListEncoder().encode(data)
    UserDefaults.standard.set(encodedImage, forKey: key)
    }
}

Now when I erase it like this:

UserDefaults.standard.set(nil, forKey: "petPhoto1")

I can still see that "Documents & Data" for my app under Settings is still full with the same size as the original image, indicating that it didn't actually delete it, even though it no longer displays when it gets loaded back from UserDefaults.

Can anyone figure out a way to fix this? Thanks!

By the way, in case it helps, here is other code related to this issue:

The code I use in the ImageViewController that I display the image after saving it:

@IBOutlet weak var imageView: UIImageView!

override func viewDidLoad() {
        super.viewDidLoad()
        
        activePet = UserDefaults.standard.string(forKey: "activePet")! // activePet = 1 (confirmed with debugging with other, unrelated code
        
        imageView.image = try? UIImage.loadFromUserDefaults(with: "petPhoto\(activePet)")
        
    }

extension UIImage {
    static func loadFromUserDefaults(with key: String) throws -> UIImage? {
         let activePet = UserDefaults.standard.string(forKey: "activePet")!
         guard let data = UserDefaults.standard.data(forKey: "petPhoto\(activePet)") else {
             return nil
         }
         do {
             let decodedImageData = try PropertyListDecoder().decode(Data.self, from: data)
             return UIImage(data: decodedImageData)
         } catch let error {
             throw error
         }
     }
}
  • 1
    Have you considered using [`removeObject(forKey:)`](https://developer.apple.com/documentation/foundation/userdefaults/1411182-removeobject)? – MadProgrammer Sep 11 '21 at 23:22
  • Sadly, the same thing ended up happening. It just doesn't shrink in size on the "iPhone Storage" tab, even though it should shrink by about 5MB. –  Sep 11 '21 at 23:34
  • So, I did a quick test (in plagrounds) ([gist is here](https://gist.github.com/RustyKnight/61e5da25c74b526257d121698d9d058d)) and I noted that the plist file did increase and decrease in size. It's possible that the observed lack of size changes may be associated with caching of the original image, but I'm speculating - You'd have to pull the app container from device and inspect it – MadProgrammer Sep 11 '21 at 23:46
  • Is there a way that you're aware of to clear the cache perhaps? –  Sep 11 '21 at 23:48
  • Assuming that the image is been stored in the cache, it will be available to the system for recycling if the system needs to re-claim space – MadProgrammer Sep 11 '21 at 23:52
  • Alternatively, you could just delete the contents (of the cache directory), but this might have unforeseen consequences – MadProgrammer Sep 11 '21 at 23:57
  • How could I delete the contents of the cache directory? –  Sep 12 '21 at 00:08
  • The best solution would be google it (I did), as it involves a two step process. `FileManager` itself doesn't support removing non-empty directories, so you'd need to recursively loop through all the contents yourself – MadProgrammer Sep 12 '21 at 00:11
  • That honestly seems scary for me as an intermediate dev Any links you could provide? All I found was stuff for WebView. –  Sep 12 '21 at 02:04
  • Just found a few links myself, I'll go look into it! :) –  Sep 12 '21 at 02:11
  • https://stackoverflow.com/questions/39004124/what-are-the-best-practice-for-clearing-cache-directory-in-ios is the one I was looking at – MadProgrammer Sep 12 '21 at 02:29
  • Why store image in UserDefault ? You could store image in other app directory and then delete it when to want. Also you could just save the image file path in the userdefault to not have to build it again when you want to load or delete it. – Ptit Xav Sep 12 '21 at 06:38
  • I’d love to do that, how do I though? I looked into it and I couldn’t find much, could you point me to a tutorial. –  Sep 12 '21 at 07:26

1 Answers1

0

When you do this:

UserDefaults.standard.set(nil, forKey: "petPhoto1")

The link between the key and the file saved will be removed synchronously. that means if you try to access the value for this key, it gives nil.

But this image needs to be cleared from storage too, that will be happening asynchronously [we don't have completion handler API support from apple to get this information].

Apple Documentation for reference:

At runtime, you use UserDefaults objects to read the defaults that your app uses from a user’s defaults database. UserDefaults caches the information to avoid having to open the user’s defaults database each time you need a default value. When you set a default value, it’s changed synchronously within your process, and asynchronously to persistent storage and other processes.

What you can try:

First approch

Give some time for the file delete operation to get completed by OS. then try to access the image in the disk.

Second approch

Try observing to the changes in the directory using GCD. Refer: https://stackoverflow.com/a/26878163/5215474

Saranjith
  • 11,242
  • 5
  • 69
  • 122