Swift 4+ Adding/Updating/Deleting Images Inside Realm and in Documents Directory


#1

Hopefully this isn’t covered and I just couldn’t find it but I did search for quite some time. I am fairly new to Realm and was having some issues finding out how to do it. I first started out by saving the images inside my Realm database but after seeing this is not the best practice I then figured out how to save the file path to Realm and save in documents directory. This may not be the best implementation and my whole point it to provide others trying to figure this out with examples.

(1) SAVE IMAGE TO REALM
-> Here is my object
class Photo: Object {
@objc dynamic var photoID = UUID().uuidString
@objc dynamic var photoName = “”
@objc dynamic var photoImg: NSData? = nil

override static func primaryKey() -> String? {
    return "photoID"
}

}

-> Here is my helper file
class RealmHelper {
static let realm = try! Realm()

static func getAllPhotos() -> Results<Photo>? {
    var photos: Results<Photo>?
    photos = realm.objects(Photo.self)
    let sortedPhotos = photos?.sorted(byKeyPath: "photoName", ascending: true)
    return sortedPhotos
}

static func savePhoto(photo: Photo) {
    do {
        try realm.write {
            realm.add(photo)
        }
    } catch {
        print("Error Saving Loan: \(error.localizedDescription)")
    }
}

static func updatePhoto(photo: Photo) {
    let id = photo.photoID
    let image = photo.photoImg
    let name = photo.photoName
    
    do {
        try realm.write {
            realm.create(Photo.self, value: [
                "photoID": id,
                "photoImg": image!,
                "photoName": name], update: true)
        }
    } catch {
        print("Error Updating Photos: \(error.localizedDescription)")
    }
}

static func deletePhoto(photo: Photo) {
    do {
        try realm.write {
            realm.delete(photo)
        }
    } catch {
        print("Error Deleting Photo: \(error.localizedDescription)")
    }
}

}

-> Load image into tableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if let index = photos {
let currentPhoto = index[indexPath.row]
if let photo = currentPhoto.photoImg {
cell.imageView?.image = UIImage(data: photo as Data)
}
cell.textLabel?.text = currentPhoto.photoName
}
return cell
}

-> Here is my save image
@IBAction func saveBtnPressed(_ sender: UIBarButtonItem) {
let newPhoto = Photo()
if let title = photoText.text {
newPhoto.photoName = title
}
if let image = photoImageView.image?.jpegData(compressionQuality: 0.5) {
newPhoto.photoImg = image as NSData
}
RealmHelper.savePhoto(photo: newPhoto)
navigationController?.popViewController(animated: true)
}

-> To edit Image

  • I first pass the selected photo to the updateVC
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == “updatePhoto” {
    if let destinationVC = segue.destination as? UpdatePhotoVC {
    if let passedPhoto = sender as? Photo {
    destinationVC.passedPhoto = passedPhoto
    }
    }
    }
    }

  • On update VC
    var passedPhoto: Photo?
    @IBAction func addBtnPressed(_ sender: UIBarButtonItem) {
    if let photo = passedPhoto {
    let updatePhoto = Photo()
    updatePhoto.photoID = photo.photoID
    if let image = photoImageView.image?.jpegData(compressionQuality: 0.5) {
    updatePhoto.photoImg = image as NSData
    }
    if let title = photoText.text {
    updatePhoto.photoName = title
    }
    RealmHelper.updatePhoto(photo: updatePhoto)
    navigationController?.popViewController(animated: true)
    }
    }

-> To Delete Image
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
if let photoToDelete = photos {
let selectedPhoto = photoToDelete[indexPath.row]
RealmHelper.deletePhoto(photo: selectedPhoto)
loadPhotos()
}
}
}

(2) SAVE IMAGE TO DOCUMENT DIRECTORY AND SAVE PATH IN REALM
-RealHelper basically same as above

-> Load image and text into tableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if let index = photos {
let currentPhoto = index[indexPath.row]
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsPath = paths[0]
let filePath = URL(fileURLWithPath: documentsPath).appendingPathComponent(currentPhoto.photoPath).path
print(“RETRIEVE PATH”)
print(filePath as Any)
cell.imageView!.image = UIImage(contentsOfFile: filePath)
cell.textLabel?.text = currentPhoto.photoDescription
}
return cell
}

-> On the save I want each image to have a unique name, I guess I could have used date but went with uuid and I also wanted to have an images folder.
@IBAction func saveBtnPressed(_ sender: UIBarButtonItem) {
let imageData = photoImageView.image?.jpegData(compressionQuality: 0.5)
let uuid = UUID().uuidString
let fileName = “(uuid).jpg”
let folderName = “ImagesFolder”
saveImageToDirectory(imageData: imageData!, fileName: fileName, folderName: folderName)
}

func saveImageToDirectory(imageData: Data, fileName: String, folderName: String) {
    let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
    let documentsDirectory = paths.object(at: 0) as! NSString
    let path = documentsDirectory.appendingPathComponent(folderName) as NSString
    if !FileManager.default.fileExists(atPath: path as String) {
        do {
            try FileManager.default.createDirectory(atPath: path as String, withIntermediateDirectories: true, attributes: nil)
        } catch let error as NSError {
            print(error.localizedDescription);
        }
    }
    let imagePath = path.appendingPathComponent(fileName)
    print("SAVE PATH")
    print(imagePath as Any)
    if !FileManager.default.fileExists(atPath: imagePath as String) {
        try? imageData.write(to: URL(fileURLWithPath: imagePath))
    }
    let newPhoto = Photo()
    if let description = photoText.text {
        newPhoto.photoDescription = description
    }
    let photoPath = "ImagesFolder/\(fileName)"
    newPhoto.photoPath = photoPath
    RealmHelper.savePhoto(photo: newPhoto)
    navigationController?.popViewController(animated: true)
}

-> Update photo
func saveImageToDirectory(imageData: Data, fileName: String, folderName: String) {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as NSArray
let documentsDirectory = paths.object(at: 0) as! NSString
let path = documentsDirectory.appendingPathComponent(folderName) as NSString
if !FileManager.default.fileExists(atPath: path as String) {
do {
try FileManager.default.createDirectory(atPath: path as String, withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
print(error.localizedDescription);
}
}
let imagePath = path.appendingPathComponent(fileName)
if !FileManager.default.fileExists(atPath: imagePath as String) {
try? imageData.write(to: URL(fileURLWithPath: imagePath))
} else if FileManager.default.fileExists(atPath: imagePath as String) {
try? imageData.write(to: URL(fileURLWithPath: imagePath))
}
if let photo = passedPhoto {
let newPhoto = Photo()
newPhoto.photoID = photo.photoID
if let description = photoText.text {
newPhoto.photoDescription = description
}
let photoPath = “ImagesFolder/(fileName)”
newPhoto.photoPath = photoPath
RealmHelper.updatePhoto(photo: newPhoto)
navigationController?.popViewController(animated: true)
}
}

-> Delete Photo
func deleteImageFromDocumentDir(localPathName: String) {
let filemanager = FileManager.default
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask,true)[0] as NSString
let destinationPath = documentsPath.appendingPathComponent(localPathName)
do {
try filemanager.removeItem(atPath: destinationPath)
} catch let error as NSError {
print(“Error Deleting Image from Documents Directory: (error.localizedDescription)”)
}
}

func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
if let photoToDelete = photos {
let selectedPhoto = photoToDelete[indexPath.row]
print(“Delete at Path: (selectedPhoto.photoPath)”)
deleteImageFromDocumentDir(localPathName: “(selectedPhoto.photoPath)”)
RealmHelper.deletePhoto(photo: selectedPhoto)
loadPhotos()
}
}
}


#2

Thank you for the contribution! It would be super cool if you could format the code for readability.


#3

You are right Jay. I went ahead and added the two project to GitHub and am including links below.