Saving image and then loading it in Swift (iOS)
Asked Answered
N

11

60

I am saving an image using saveImage.

func saveImage (image: UIImage, path: String ) -> Bool{

    let pngImageData = UIImagePNGRepresentation(image)
    //let jpgImageData = UIImageJPEGRepresentation(image, 1.0)   // if you want to save as JPEG

    print("!!!saving image at:  \(path)")

    let result = pngImageData!.writeToFile(path, atomically: true)

    return result
}

New info:

Saving file does not work properly ("[-] ERROR SAVING FILE" is printed)--

            // save your image here into Document Directory
        let res = saveImage(tempImage, path: fileInDocumentsDirectory("abc.png"))
        if(res == true){
            print ("[+] FILE SAVED")
        }else{
            print ("[-] ERROR SAVING FILE")
        }

Why doesn't the saveImage function save the image? Access rights?

Older info:

The debug info says:

!!!saving image at:  file:///var/mobile/Applications/BDB992FB-E378-4719-B7B7-E9A364EEE54B/Documents/tempImage

Then I retrieve this location using

fileInDocumentsDirectory("tempImage")

The result is correct.

Then I am loading the file using this path

    let image = UIImage(contentsOfFile: path)

    if image == nil {

        print("missing image at: \(path)")
    }else{
        print("!!!IMAGE FOUND at: \(path)")
    }

The path is correct, but the message is "missing image at..". Is the file somehow inaccessible or not stored? What can be a reason for this behavior?

I am testing this code on iphone 4 with ios 7 and iphone 5 with ios 7 simulator.

Edit: 1. The fileInDocumentsDirectory function

func fileInDocumentsDirectory(filename: String) -> String {

    let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
    let fileURL = documentsURL.URLByAppendingPathComponent(filename).absoluteString
    return fileURL        
}
Naphthyl answered 20/5, 2016 at 10:52 Comment(9)
Check the image in finder, It's available or not. and add some extensions to image *.png or *.jpeg.Scheller
dont save full path of image just save the image name only and append the name with real time document directory-path. that's it because document directory path never return same with each run.Bureaucratize
@NitinGohel I just saving the file and then trying to load it like that loadImageFromPath(fileInDocumentsDirectory("abc.png"))Naphthyl
fineInDocumentDirectory is the realtime path or you load from database?Bureaucratize
@NitinGohel I added the code of this function. Just three lines.Naphthyl
simply just follow the : hackingwithswift.com/example-code/media/…Bureaucratize
@NitinGohel, Unfortunately that cannot work in Swift 2.0, because stringByAppendingPathComponent is deprecated.Naphthyl
Be careful about where you save images and other data. Documents/ should be used for user created data. Look at this guide: #37332029Grantley
@Joakim Thank you. It looks like I want to use the Documents directory, because I am storing user profile images.Naphthyl
P
77

This function will save an image in the documents folder:

func saveImage(image: UIImage) -> Bool {
    guard let data = UIImageJPEGRepresentation(image, 1) ?? UIImagePNGRepresentation(image) else {
        return false
    }
    guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) as NSURL else {
        return false
    }
    do {
        try data.write(to: directory.appendingPathComponent("fileName.png")!)
        return true
    } catch {   
        print(error.localizedDescription)
        return false
    }
}

To use:

let success = saveImage(image: UIImage(named: "image.png")!)

This function will get that image:

func getSavedImage(named: String) -> UIImage? {
    if let dir = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) {
        return UIImage(contentsOfFile: URL(fileURLWithPath: dir.absoluteString).appendingPathComponent(named).path)
    }
    return nil
}

To use:

if let image = getSavedImage(named: "fileName") {
    // do something with image
}
Paulo answered 4/6, 2017 at 13:50 Comment(2)
Works for **Swift 4.2 ** as well! :)Montgolfier
what to do with let success ? how can i store or insert or would you give an example to save on a button click and press on other button should show in image viewStronski
H
61

iOS 13+ Swift 5.1

Works with iOS 17 and Swift 5.9

iOS 12 introduced some API Changes.

func saveImage(imageName: String, image: UIImage) {
    
    
 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
}
Hornstone answered 22/12, 2018 at 9:19 Comment(0)
R
8

Details

  • Xcode Version 10.2 (10E125), Swift 5

Solution

// save
extension UIImage {

    func save(at directory: FileManager.SearchPathDirectory,
              pathAndImageName: String,
              createSubdirectoriesIfNeed: Bool = true,
              compressionQuality: CGFloat = 1.0)  -> URL? {
        do {
        let documentsDirectory = try FileManager.default.url(for: directory, in: .userDomainMask,
                                                             appropriateFor: nil,
                                                             create: false)
        return save(at: documentsDirectory.appendingPathComponent(pathAndImageName),
                    createSubdirectoriesIfNeed: createSubdirectoriesIfNeed,
                    compressionQuality: compressionQuality)
        } catch {
            print("-- Error: \(error)")
            return nil
        }
    }

    func save(at url: URL,
              createSubdirectoriesIfNeed: Bool = true,
              compressionQuality: CGFloat = 1.0)  -> URL? {
        do {
            if createSubdirectoriesIfNeed {
                try FileManager.default.createDirectory(at: url.deletingLastPathComponent(),
                                                        withIntermediateDirectories: true,
                                                        attributes: nil)
            }
            guard let data = jpegData(compressionQuality: compressionQuality) else { return nil }
            try data.write(to: url)
            return url
        } catch {
            print("-- Error: \(error)")
            return nil
        }
    }
}

// load from path

extension UIImage {
    convenience init?(fileURLWithPath url: URL, scale: CGFloat = 1.0) {
        do {
            let data = try Data(contentsOf: url)
            self.init(data: data, scale: scale)
        } catch {
            print("-- Error: \(error)")
            return nil
        }
    }
}

Usage

// save image (way 1)
let path = "photo/temp/album1/img.jpg"
guard   let img = UIImage(named: "img"),
        let url = img.save(at: .documentDirectory,
                           pathAndImageName: path) else { return }
print(url)

// get image from directory
guard let img2 = UIImage(fileURLWithPath: url) else { return }

// save image (way 2)
let tempDirectoryUrl = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(path)
guard let url2 = img2.save(at: tempDirectoryUrl) else { return }
print(url2)

Check results

open the iOS simulator directory

Roderick answered 26/4, 2019 at 21:15 Comment(0)
Q
4

You should save image name with extension so your path should be like,

///var/mobile/Applications/BDB992FB-E378-4719-B7B7-E9A364EEE54B/Documents/tempImage.png

And second thing replace below line,

   let result = pngImageData!.writeToFile(path, atomically: true)

with

    let result = pngImageData!.writeToFile(path, atomically: false)

You need to set false as parameter of atomically.

atomically:

If true, the data is written to a backup file, and then—assuming no errors occur—the backup file is renamed to the name specified by path; otherwise, the data is written directly to path.

Hope this will help :)

Quickfreeze answered 20/5, 2016 at 11:43 Comment(8)
Hi, I did that, but it still does not work -- !!!save: file:///var/mobile/Applications/E78F2521-929E-4E4D-834F-57C07A62DB84/Documents/tempImage.png !!!miss: file:///var/mobile/Applications/E78F2521-929E-4E4D-834F-57C07A62DB84/Documents/tempImage.png !!!miss: file:///var/mobile/Applications/E78F2521-929E-4E4D-834F-57C07A62DB84/Documents/tempImage.pngNaphthyl
Make sure your image is not nil!! and you are passing path in let image = UIImage(contentsOfFile: path) is exact same as you write it and this path should be with extension. And once try to retrieve in NSData instead of UIImage from this path. If you got data then that means there is a problem to convert it in image.Quickfreeze
Btw. I am using this code #30953570 -- from the @dharmesh-kheni 's answer.Naphthyl
try to save image like this saveImage(tempImage, path: fileInDocumentsDirectory("tempImage.png")) from that answer.Quickfreeze
I'm doing exactly that now and it still does not work.Naphthyl
It should work!! strange if not work!! once try to change different name like abc.png instead tempImage.pngQuickfreeze
loadImageFromPath(fileInDocumentsDirectory("abc.png")) returns null when saveImage(tempImage, path: fileInDocumentsDirectory("abc.png")) used.Naphthyl
This is how it looks like from my perspective !!!save: file:///var/mobile/Applications/AE7C3783-C25C-4C4A-8ABB-650400CB32A7/Documents/abc.png !!!miss: file:///var/mobile/Applications/AE7C3783-C25C-4C4A-8ABB-650400CB32A7/Documents/abc.pngNaphthyl
D
4

Save image in local Xcode Documents directory

Pass in your image and the name you want to call it (you choose what you want fileName to be).

func saveImageLocally(image: UIImage, fileName: String) {
    
 // Obtaining the Location of the Documents Directory
    let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    
    // Creating a URL to the name of your file
    let url = documentsDirectory.appendingPathComponent(fileName)
    
    if let data = image.pngData() {
        do {
            try data.write(to: url) // Writing an Image in the Documents Directory
        } catch {
            print("Unable to Write \(fileName) Image Data to Disk")
        }
    }
}

Read

Use the same fileName as when you saved it

func getImageFromName(fileName: String) {
    let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let url = documentsDirectory.appendingPathComponent(fileName)
    
    if let imageData = try? Data(contentsOf: url) {
        let image = UIImage(data: imageData) // HERE IS YOUR IMAGE! Do what you want with it!
        
    } else {
        print("Couldn't get image for \(fileName)")
    }
}
Decolorant answered 21/12, 2021 at 18:48 Comment(0)
P
1

Ashish's comment has a clue to the answer. If you read the docs on UIImage(contentsOfFile:) they say

path The path to the file. This path should include the filename extension that identifies the type of the image data.

The imageNamed call is smart enough to try the .png and .jpg extensions, but the contentsOfFile call expects a full path including extension.

Priceless answered 20/5, 2016 at 11:4 Comment(1)
I added the extension, but that hasn't helped so far. Btw. I am using this code #30953570 -- from the @dharmesh-kheni 's answer.Naphthyl
M
1

If you want to load image from server you can do like below

 let url = URL(string: "http://live-wallpaper.net/iphone/img/app/i/p/iphone-4s-wallpapers-mobile-backgrounds-dark_2466f886de3472ef1fa968033f1da3e1_raw_1087fae1932cec8837695934b7eb1250_raw.jpg")
        URLSession.shared.dataTask(with: url!) { (data, response, error) in
            guard
                let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
                let data = data, error == nil
                else { return }
                DispatchQueue.main.async() { () -> Void in
                let fileManager = FileManager.default
                let paths = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("apple.jpg")
                print(paths)
                fileManager.createFile(atPath: paths as String, contents: data, attributes: nil)

            }}.resume()
Mussorgsky answered 19/11, 2016 at 5:29 Comment(1)
he means to load & retrive from locallyPerch
C
1

Swift 5

func saveImage(image: UIImage) -> Bool{
    guard let data = image.jpegData(compressionQuality: 1) ?? image.pngData() else {
        return false
    }
    guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) as NSURL else {
        return false
    }
    do{
        try data.write(to: directory.appendingPathComponent("\(txtNom.text!).png")!)
        print(directory)
        print(data)
        print("si se pudo")
        return true
    } catch {
        print(error.localizedDescription)
        return false
    }
} // saveImage
Connive answered 10/7, 2019 at 17:33 Comment(1)
You should add comments or a description of what is going on in your answerBottoms
L
1

You can actually use PHPhotoLibrary to do that. Here is the code for saving the image and fetching the image url.

extension UIImage {
func saveToPhotoLibrary(completion: @escaping (URL?) -> Void) {
    var localeId: String?
    PHPhotoLibrary.shared().performChanges({
        let request = PHAssetChangeRequest.creationRequestForAsset(from: self)
        localeId = request.placeholderForCreatedAsset?.localIdentifier
    }) { (isSaved, error) in
        guard isSaved else {
            debugPrint(error?.localizedDescription)
            completion(nil)
            return
        }
        guard let localeId = localeId else {
            completion(nil)
            return
        }
        let fetchOptions = PHFetchOptions()
        fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
        let result = PHAsset.fetchAssets(withLocalIdentifiers: [localeId], options: fetchOptions)
        guard let asset = result.firstObject else {
            completion(nil)
            return
        }
        getPHAssetURL(of: asset) { (phAssetUrl) in
            completion(phAssetUrl)
        }
    }
}

static func getPHAssetURL(of asset: PHAsset, completionHandler : @escaping ((_ responseURL : URL?) -> Void))
    {
            let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
            options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
                return true
            }
            asset.requestContentEditingInput(with: options, completionHandler: { (contentEditingInput, info) in
                completionHandler(contentEditingInput!.fullSizeImageURL)
            })

    }
}
Limoli answered 29/4, 2020 at 15:2 Comment(0)
N
0

You have to create a directory in the Documents directory to be able to store a file.

Naphthyl answered 20/5, 2016 at 14:15 Comment(2)
That's just not true. You can certainly create a directory inside the sandboxed Documents directory and save your images there, but you don't have to do so. It works just fine to save image directly into the Documents directory.Priceless
@DuncanC Thank you. Good to know that, but it currently works for me. I had a problem without this.Naphthyl
C
0

I found the solution on StackOverFlow some time ago. I didn't remember the author

Assuming yourImage is UIImage()

let ciImage = yourImage!.ciImage
let context = CIContext()
let cgImage = context.createCGImage(ciImage!, from: ciImage!.extent)
let uiImage = UIImage(cgImage: cgImage!)

UIImageWriteToSavedPhotosAlbum(uiImage, self, 
#selector(self.image(_:didFinishSavingWithError:contextInfo:)), nil)

and this function

@objc func image(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
if let error = error {
    // we got back an error!
    let ac = UIAlertController(title: "Save error", message: error.localizedDescription, preferredStyle: .alert)
    ac.addAction(UIAlertAction(title: "OK", style: .default))
    present(ac, animated: true)
} else {
    let ac = UIAlertController(title: "Saved!", message: "Your altered image has been saved to your photos.", preferredStyle: .alert)
    ac.addAction(UIAlertAction(title: "OK", style: .default))
    present(ac, animated: true)
}

}

Containment answered 6/9, 2019 at 0:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.