Search code examples
swiftxcodeswiftuicore-datauiimage

Is there a way to perpetually save photos to an Xcode app after refreshing?


I'm working on a program that uploads images to a CollectionView by accessing the user's photo library. It's looking great so far, and the majority of it is running smoothly.

However, when I exit out of the app and refresh it, the once uploaded photos don't appear anymore.

I'm only a little familiar with Core Data and I've been trying to work it in, but all of my sources aren't that helpful.

Here's my code so far:

import UIKit
import PhotosUI
import Photos
import CoreData

class ViewController: UIViewController, PHPickerViewControllerDelegate {

    @IBOutlet var collectionView: UICollectionView!
    
                                
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // set up collection
        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: 125, height: 125)
        collectionView.collectionViewLayout = layout
        
        layout.minimumInteritemSpacing = 10
        layout.minimumLineSpacing = 10
    
        
    }
    
    
    // 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
                }
                // add to user's photo array
                print(image)
                imageArray.append(image)
                
            }
        }
        
        group.notify(queue: .main) {
            self.collectionView.reloadData()
        }
        
        
    }
    

}


// user images below
var imageArray = [UIImage]()


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
        
        
        cell.imageView.image = 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)
    }
    
}

I don't know where to start! Any ideas?


Solution

  • For your question, reason your app lost all photos everytime you refresh because it was temporary store but not save somewhere else ( internet or on local).

    If you want to save it on you own server, create server side and do with api.

    Come to local here, you have come to the right path finding a local which is Core Data to store. But the key point to need to know here is:

    • Saving image to local should use FileManager ( which means each app have the own documentary folder in your phone so save the not only image but document, files, everything else to here - It will lost only when your app is uninstalled) You just need to keep the name of the image ( you can create your own name to save image)

    Code will be like this

    func saveImage(imageName: String, image: UIImage) {
        // get documentary path to save image to
        guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
    
        let fileName = imageName
        let fileURL = documentsDirectory.appendingPathComponent(fileName)
        guard let data = image.jpegData(compressionQuality: 1) else { return }
    
        //Checks if file exists, removes it if so.
        if FileManager.default.fileExists(atPath: fileURL.path) {
            do {
                try FileManager.default.removeItem(atPath: fileURL.path)
                print("Removed old image")
            } catch let removeError {
                print("couldn't remove file at path", removeError)
            }
    
        }
        
        do {
            try data.write(to: fileURL)
        } catch let error {
            print("error saving file with error", error)
        }
    }
    
    func loadImageFromDiskWith(fileName: String) -> UIImage? {
      let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
    
        let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
        let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
    
        if let dirPath = paths.first {
            let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
            let image = UIImage(contentsOfFile: imageUrl.path)
            return image
    
        }
    
        return nil
    }
    

    Next is the Core Data or UserDefaults is where you save the key connect to the list of name images you save before. So everytime you come into app, just take the list image name out from the key and appear it again

    I will do with UserDefaults you can try to implement the same with CoreData. I save image as name is number so I save to UserDefault only the number of images. You can change to something like you want

    Code will be like this

    var imageArray = [UIImage]()
    var countImage = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // get number of image have been saved
        let countImage = UserDefaults.standard.integer(forKey: "test")
    
        if countImage < 1 {
            return
        }
        
        for i in 1...countImage {
            // get image from disk
            imageArray.append(self.loadImageFromDiskWith(fileName: String(i))!)
        }
        
        print(imageArray)
        self.collectionView.reloadData() // reload collection view
    }
    
    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
                }
                // add to user's photo array
                print(image)
                self.imageArray.append(image)
                // append self.countImage to 1 and save number to  UserDefault and image to FileManager
                self.countImage += 1
                UserDefaults.standard.set(self.countImage, forKey: "test")
                self.saveImage(imageName: String(self.countImage), image: image)
            }
        }
        
        group.notify(queue: .main) {
            self.collectionView.reloadData()
        }
    }