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)
}
}
UserDefaultsto store arrays of images. It is meant to be used for small size user preferences key value pairs.UserDefaultsto 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 inUserDefaultsor 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 useUserDefaults? It has a good documentation, what are you struggling with?UserDefaultsinto 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 thepickerfunction. Thanks for your help! :DPersisting File ReferencesinUserDefaults, Apple recommends usingbookmark, see: developer.apple.com/documentation/foundation/nsurl/…