0

I'm working on a project where the user can upload images from their library into a gallery. It's working fine, but I want the images to stay even after I refresh the app. I've been told that NSUserData will actually work better than CoreData, but I'm having a LOT of trouble implementing it into my code. I've never worked with UserData, so I am completely lost. Any help would be appreciated!

View Controller:

//  ViewController.swift


import UIKit
import PhotosUI
import Photos
import CoreData
import Foundation


// user images below
var imageArray = [UIImage]()
var imageIDs = [""]
var countImage = 0


class ViewController: UIViewController, PHPickerViewControllerDelegate {

    
    let userDefaults = UserDefaults.standard
    
    
    @IBOutlet var collectionView: UICollectionView!
    
    // private let myFileManager = LocalFileManager.instance
    // private let folderName = "closet_folder"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        // set up collection in closet
        navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(addPhotos))
        
        
        collectionView.register(ClosetCollectionViewCell.nib(), forCellWithReuseIdentifier: "ClosetCollectionViewCell")
        
        collectionView.delegate = self
        collectionView.dataSource = self
        
        let layout = UICollectionViewFlowLayout()
        layout.itemSize = CGSize(width: 115, height: 115)
        collectionView.collectionViewLayout = layout
        
        layout.minimumInteritemSpacing = 6
        layout.minimumLineSpacing = 7
            
    }


    
    // access photo library
    @objc private func addPhotos() {
        var config = PHPickerConfiguration()
        config.selectionLimit = 10
        config.filter = .images
        let vc = PHPickerViewController(configuration: config)
        vc.delegate = self
        present(vc, animated: true)
    }
    
    func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
        picker.dismiss(animated: true, completion: nil)
        let group = DispatchGroup()
        
        results.forEach { result in
            group.enter()
            result.itemProvider.loadObject(ofClass: UIImage.self) { reading, error in
                defer {
                    group.leave()
                }
                guard let image = reading as? UIImage, error == nil else {
                    return
                }
                print(image)
                imageArray.append(image)
                
                // imageIDs.append("\(image)")
                countImage += 1
                print(countImage)
                imageIDs.append(String(countImage))
                // imageIDs.append("\(image)")
                LocalFileManager.instance.saveImage(image: image, imageName: String(countImage), folderName: "closet_folder")
            
            }
        }
        
        group.notify(queue: .main) {
            self.collectionView.reloadData()
        }
        
        
    }
    
}


extension ViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        collectionView.deselectItem(at: indexPath, animated: true)
        print("you tapped me!")
        // when cell is tapped...
    }
}

extension ViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        // how many cells are shown? based on number of items the user uploaded
        return imageArray.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        // return cell for given item
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ClosetCollectionViewCell", for: indexPath) as! ClosetCollectionViewCell
        // show every cell in imageArray
        cell.imageView.image = LocalFileManager.instance.getImage(imageName: imageIDs[indexPath.row + 1], folderName: "closet_folder")
        //imageArray[indexPath.row]
        
        return cell
    }
}

extension ViewController: UICollectionViewDelegateFlowLayout {
    // margin of padding between cells
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: 115, height: 115)
    }
    
}

Here's the code with the saveImage and getImage functions: It generates URLs for every image and the folder it's stored in.

//
//  LocalFileManager.swift

import Foundation
import SwiftUI

class LocalFileManager {
    static let instance = LocalFileManager()
    private init() {}
    
    
    
    func saveImage(image: UIImage, imageName: String, folderName: String) {
        // create folder
        createFolderIfNeeded(folderName: folderName)
        
        // get path for image
        guard
            let data = image.pngData(),
            let url = getURLforImage(imageName: imageName, folderName: folderName)
        else { return  }
        
        // save image to path
        do {
            try data.write(to: url)
        } catch let error {
            print("image name: \(image) error saving image: \(error)")
        }
        
    }
    
    
    func getImage(imageName: String, folderName: String) -> UIImage? {
        guard let url = getURLforImage(imageName: imageName, folderName: folderName),
              FileManager.default.fileExists(atPath: url.path)
        else {
            return nil
        }
        return UIImage(contentsOfFile: url.path)
    }
    
    
    
    // referenced functions below
    
    // create folder to store images
    private func createFolderIfNeeded(folderName: String) {
        guard let url = getURLforFolder(folderName: folderName) else {
            return
        }
        if !FileManager.default.fileExists(atPath: url.path) {
            do {
                try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
            }
            catch let error {
                print("folder name: \(folderName) error creating folder! \(error)")
            }
        }
    }
    
    // get URL for that folder
    private func getURLforFolder(folderName: String) -> URL? {
        guard let url = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first
        else {
            return nil
        }
        return url.appendingPathExtension(folderName)
    }
    
    // generate URL for images in folder
    private func getURLforImage(imageName: String, folderName: String) -> URL? {
        guard let folderURL = getURLforFolder(folderName: folderName) else {
            return nil
        }
        return folderURL.appendingPathExtension(imageName)
    }
    
}

4
  • 2
    IMHO, you should not be using UserDefaults to store arrays of images. It is meant to be used for small size user preferences key value pairs. Commented Oct 13, 2022 at 6:14
  • I second that, don't use UserDefaults to store large amounts of data, it's not meant to be used like that. Save the images in a directory (you seem to do that already), then store an URL to the "current" image in UserDefaults or maybe even lists of URLs); point is, the large data should go to disk. But I wonder what the question is here: do you want to know how to use UserDefaults? It has a good documentation, what are you struggling with? Commented Oct 13, 2022 at 6:22
  • @DarkDust I guess I'm just a little confused as to how to implement UserDefaults into my code (with urls)... I know I can change the defaults with something like this UserDefaults.standard.set(url, forKey: "key") but given what I already coded, I'm not sure how to retrieve it with the picker function. Thanks for your help! :D Commented Oct 13, 2022 at 7:36
  • Note, for Persisting File References in UserDefaults, Apple recommends using bookmark, see: developer.apple.com/documentation/foundation/nsurl/… Commented Oct 13, 2022 at 7:53

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.