Saving image and then loading it in Swift (iOS)

IosSwiftCachingFile ManagerImage Caching

Ios Problem Overview


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        
    

    }

Ios Solutions


Solution 1 - Ios

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
}

Solution 2 - Ios

iOS 13+ Swift 5.1

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
}

Solution 3 - Ios

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

Solution 4 - Ios

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 :)

Solution 5 - Ios

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.

Solution 6 - Ios

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()

Solution 7 - Ios

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)")
    }
}

Solution 8 - Ios

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

Solution 9 - Ios

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

Solution 10 - Ios

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)
}

}

Solution 11 - Ios

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)
            })
        
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontesgoeView Question on Stackoverflow
Solution 1 - IosBobbyView Answer on Stackoverflow
Solution 2 - IosSam BingView Answer on Stackoverflow
Solution 3 - IosVasily BodnarchukView Answer on Stackoverflow
Solution 4 - IosKetan ParmarView Answer on Stackoverflow
Solution 5 - IosDuncan CView Answer on Stackoverflow
Solution 6 - IosguruView Answer on Stackoverflow
Solution 7 - IosHenry NoonView Answer on Stackoverflow
Solution 8 - IostesgoeView Answer on Stackoverflow
Solution 9 - IosJohn RoView Answer on Stackoverflow
Solution 10 - IosninahadiView Answer on Stackoverflow
Solution 11 - IosGurkan SoykanView Answer on Stackoverflow