0

I'm attempting to save (parse?) an image Path to my realm. To do that I attempting to save the image as jpegData into the documentDirectory.

The images I am saving do not need to be saved into the users photo library, only to be referenced and displayed in the app itself.

First off, I'm not certain if I am taking the right approach for this. Please direct me to the most appropriate solution for saving images locally.

I've seen only a handful of websites, Stack Overflow questions and hardly any videos, so something tells me there is a more efficient method to accomplish this. Saving Picked Image to CoreData, How to put an image in a Realm database? and how to use writeToFile to save image in document directory? led me to attempt to save the image into the directory, particularly the final SO Question.

I have the following code, but am receiving the error "Value of type 'MyViewControllerName' has no member jpegData" Any ideas how I can solve this error or adapt my code to allow it to save the image?

The image I am trying to save, once selected from Photo Library or Camera is located in selectedImageView.

I've changed the self.jpegData to UIImage.jpegData, selectedImageView.jpegData and to UIImageView.jpegData getting hosts of errors, including Instance member 'jpegData' cannot be used on type 'UIImage'; did you mean to use a value of this type instead?.

@objc func saveTapped(_ sender: UIBarButtonItem) {
        self.performSegue(withIdentifier: "saveTappedSegue", sender: self)
    }
    
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "saveTappedSegue" {
            let dest = segue.destination as! MyViewControllerName
            
            let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
            let fileName = "Image.jpg"
            let fileURL = documentsDirectory.appendingPathComponent(fileName)
            if let data = self.jpegData(compressionQuality: 1.0), //Error here
                !FileManager.default.fileExists(atPath: fileURL.path) {
                do {
                    try data?.write(to: fileURL!)
                    print("Image Saved")
                } catch {
                    print("error saving file:", error)
                }
            }
        
// Remaining code includes writing strings to realm not yet linked to the image.Path. This will be attempted once I can solve this error.

        }
    }

UPDATE - added more code

 public var imagePickerController: UIImagePickerController?
    
    public var defaultImageUrl: URL?
       
       internal var selectedImage: UIImage? {
           get {
               return self.selectedImageView.image
           }
           
           set {
               switch newValue {
               case nil:
                   self.selectedImageView.image = nil
                   self.selectImageButton.isEnabled = true
                   self.selectImageButton.alpha = 1
                   
                   self.removeImageButton.isEnabled = false
                   self.removeImageButton.alpha = 0
               default:
                   self.selectedImageView.image = newValue
                   self.selectImageButton.isEnabled = false
                   self.selectImageButton.alpha = 0
                   
                   self.removeImageButton.isEnabled = true
                   self.removeImageButton.alpha = 1
               }
           }
       }

and

 //image input codes
    @IBOutlet weak var selectedImageContainer: UIView!
    @IBOutlet weak var selectedImageView: UIImageView!   
    @IBOutlet weak var selectImageButton: UIButton! {
        didSet {
            guard let button = self.selectImageButton else { return }
            button.isEnabled = true
            button.alpha = 1
        }
    }
    

    @IBOutlet weak var removeImageButton: UIButton! {
        didSet {
            guard let button = self.removeImageButton else { return }
            button.isEnabled = false
            button.alpha = 0
        }
    }

Relevant info in viewDidLoad

override func viewDidLoad() {
        self.selectedImageView.contentMode = .scaleAspectFit
        self.selectImageButton.isEnabled = self.selectedImage == nil
        self.selectImageButton.alpha = 1

to save the image

//MARK: - "Select to Add Photo"
    @IBAction func selectImageButton(_ sender: Any) {
    
    if self.imagePickerController != nil {
            self.imagePickerController?.delegate = nil
            self.imagePickerController = nil
        }
        self.imagePickerController = UIImagePickerController.init()
        
        let alert = UIAlertController.init(title: "Select Source Type", message: nil, preferredStyle: .actionSheet)
        
        if UIImagePickerController.isSourceTypeAvailable(.camera) {
            alert.addAction(UIAlertAction.init(title: "Camera", style: .default, handler: { (_) in
                self.presentImagePicker(controller: self.imagePickerController!, source: .camera)
            }))
        }
        
        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
            alert.addAction(UIAlertAction.init(title: "Photo Library", style: .default, handler: { (_) in
                self.presentImagePicker(controller: self.imagePickerController!, source: .photoLibrary)
            }))
        }
 alert.addAction(UIAlertAction.init(title: "Cancel", style: .cancel))
        self.present(alert, animated: true)
    }
    
    internal func presentImagePicker(controller: UIImagePickerController , source: UIImagePickerController.SourceType) {
        controller.delegate = self
        controller.sourceType = source
        controller.allowsEditing = true
        self.present (controller, animated: true)
    }
    
    
    @IBAction func removeImageButtonAction(_ sender: UIButton) {
        self.selectedImage = nil
    }

in my extensions I have

extension AddNewMealVC: UIImagePickerControllerDelegate, UINavigationControllerDelegate {


    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
       
        if let image = info[.editedImage] as? UIImage {
            self.selectedImageView.image = image
        } else if let image = info[.originalImage] as? UIImage {
            self.selectedImageView.image = image
        }
           picker.dismiss(animated: true) {
            picker.delegate = nil
            self.imagePickerController = nil
        }
    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
         picker.dismiss(animated: true) {
            picker.delegate = nil
            self.imagePickerController = nil
        }
    }
}
24SGR
  • 51
  • 1
  • 7
  • "to save the image" Where is an image? I see no line of code suggesting that you have an image. – El Tomato Jun 24 '20 at 00:39
  • @ElTomato, I have added the relevant code for the image. It looks rather complex as I have taken adapted it from multiple sources! – 24SGR Jun 24 '20 at 07:34
  • let image = selectedImageView.image; if let data = UIImageJPEGRepresentation(image, 1.0) { try? data.write(to: fileURL as URL) } – El Tomato Jun 24 '20 at 08:34
  • Thanks @ElTomato. I think, as per nghiahoang's answer that I have partially solved it. How would I get a printout of the file path? – 24SGR Jun 24 '20 at 08:54

1 Answers1

0

jpegData is a instance method of UIImage. Let use:

selectedImageView.image?.jpegData()
nghiahoang
  • 538
  • 4
  • 10
  • I added this and got the printout "Image Saved". I tried again with a second image and it showed nothing in the Output. Would that be because it is trying to save the image as a file name that already exists? – 24SGR Jun 24 '20 at 08:53
  • yes, you have to check whether or not the file existed – nghiahoang Jun 24 '20 at 08:54
  • How would I achieve this? – 24SGR Jun 24 '20 at 08:55
  • `if FileManager.default.fileExists(atPath: path) { }` and you can remove the existed one by **FileManager.default.removeItem(atPath: path)** – nghiahoang Jun 24 '20 at 08:57
  • to confirm, my code already has `!FileManager.default.fileExists(atPath: fileURL.path)`. Is this where my code is checking for existing file? Would it be better for me to name my saved images as the current Date and Time, as all the images that will be saved on each call of the code need to be kept. – 24SGR Jun 24 '20 at 09:05
  • it depends on your requirement, if you want to save multiple images then you have to named each of them differently. The best way is, hash the jpegData to generate image name. [https://stackoverflow.com/questions/1684799/generate-hash-from-uiimage] It'll ensure that the same image isn't saved as two files. – nghiahoang Jun 24 '20 at 09:10
  • Essentially I am making a database of various string fields with 1,2 or 3 images. This code is for the data entry into the Realm for strings (not shown above) and to save the image to directory. Once I have the image saved to directory I'll take that path/URL as a string to store in realm alongside the strings of other information. It doesn't matter what the image is called, I just want to create the image in the directory as a new name and new image each time the function SaveTapped is tapped – 24SGR Jun 24 '20 at 09:16
  • then let use a random name – nghiahoang Jun 24 '20 at 09:18
  • Thanks for you help by the way! I'm a complete novice (as you can tell). I'm assuming I would assign it a random name in this section of code, `let fileName = "Image.jpg"`. How would I write the code to do so? – 24SGR Jun 24 '20 at 09:21
  • You can refer https://stackoverflow.com/questions/26845307/generate-random-alphanumeric-string-in-swift – nghiahoang Jun 24 '20 at 09:24
  • Then change “image.jpg” to “\(randomString(20)).jpg” – nghiahoang Jun 24 '20 at 09:25
  • Thank you. If the app is to be used on one device locally, would you recommend the randomString() method as opposed to `let fileName = UUID().uuidString`? I'm uncertain how long the UUIDString would be. – 24SGR Jun 24 '20 at 09:28
  • uuid is fine to use. It has 36 characters length – nghiahoang Jun 24 '20 at 09:30
  • Thank you for your help! I've now got the image saving each time and the output printing the URL :D – 24SGR Jun 24 '20 at 09:34