I am currently designing a database management application with Realm, where I have managed to create and retrieve an object successfully. The problem I am having is with updating/editing - specifically updating the UIImage that the user has uploaded. With Realm, I save the path of the image and then retrieve it by loading that path (in Documents Directory).
When the user tries to save the changed image, for some odd reason the UIImageJPEGRepresentation saves the changed image as nil, thus removing the user's image. It's strange because the initial creation of a data object stores it just fine.
I have tried to check whether the image is being passed correctly with some debugging, and have found that it does so just fine and the right path is being saved on.
Here is my update method:
func updateImage() {
let documentsDirectoryURL = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileURL = documentsDirectoryURL.appendingPathComponent("\(selectedPicPath!)")
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
if profilePic.image != nil {
let image = profilePic.image!.generateJPEGRepresentation()
try! image.write(to: fileURL, options: .atomicWrite)
}
} catch {
print(error)
}
} else {
print("Image Not Added")
}
}
Can anyone see any problems?
let image = profilePic.image!.generateJPEGRepresentation()
Check this line, whether it is returning nil value or data? If nil, then use following code to test your image store, it's working. Also ensure your actual image has JPEG file format extension, that you are trying to generate.
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
// For PNG Image
if let image = UIImage(named: "example.png") {
if let data = UIImagePNGRepresentation() {
let filename = getDocumentsDirectory().appendingPathComponent("copy.png")
try? data.write(to: filename)
}
}
For JPG image
if let image = UIImage(named: "example.jpg") {
if let data = UIImageJPEGRepresentation(image, 1.0) {
let filename = getDocumentsDirectory().appendingPathComponent("copy.jpg")
try? data.write(to: filename)
}
}
Related
i'm saving firebase image in document directory !! For uniqueness my Document Directory name is firebase image name ! i have check with a condition that if firebase image name is not exists in my document directory then save that image in Document Directory !!
i'm checking if that firebase name means it save in document directory then it get document directory image if not then it get image from Firebase !!
Issue is when i try to get image :-
(1) For example :- Firebase Image name is 1.jpg .
(2) Document Directory save image with firebase name like 1.jpg .
(3) now i change firebase image to other but it save with name 1.jpg .
(4) when i try to get image because already 1.jpg is in Document Directory that's why it not return me updated image it show me previous 1.jpg image !!
how can i solved this issue. Thank You
Save And Get Image Code :-
func saveImageDocumentDirectory(imageName: String){
let documentPath = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let imgURL = documentPath.appendingPathComponent(imageName, isDirectory: true)
if !FileManager.default.fileExists(atPath: imgURL.path){
do{
let image = UIImage(named: imgURL.path)
try image?.pngData()?.write(to: imgURL)
}catch let err{
print("error in save:\(err.localizedDescription)")
}
}
}
func getDocumentImage(imageName: String, firebaseImgURL: URL, imgView:UIImageView){
let documentPath = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let imgURL = documentPath.appendingPathComponent(imageName, isDirectory: true)
let image = UIImage(contentsOfFile: imgURL.path)
if image != nil{
imgView.image = image
}else{
imgView.kf.setImage(with: firebaseImgURL)
}
}
Please try with like below code
Save Data
let placeholderURL: URL =
getDocumentDirectoryPath()!.appendingPathComponent("\(imageName.JPG)/",
isDirectory: true)
let fileExists: Bool = FileManager.default.fileExists(atPath: placeholderURL.path )
if !fileExists {
let thumbnailImageURL = URL(string: firebaseURL)
var placeholderData: Data? = nil
if let url = url {
do {
placeholderData = try Data(contentsOf: (thumbnailImageURL ?? nil)!)
} catch {
print("Unable to load data: \(error)")
}
}
if urlData != nil {
do {
//saving is done on main thread
try placeholderData?.write(to: URL(fileURLWithPath: placeholderURL.path) , options: .atomic)
} catch {
print(error)
}
}
}
Retrive image
let thumbnailURL: URL = getDocumentDirectoryPath()!.appendingPathComponent("\("imageName.JPG")/", isDirectory: true)
let fileExists: Bool = FileManager.default.fileExists(atPath: thumbnailURL.path)
var urlThumbnail:URL? = nil
if fileExists {
urlThumbnail = URL(fileURLWithPath: thumbnailURL.path)
} else {
urlThumbnail = URL(string: firebaseURL)
}
Sat image in image view
self.imgtemp.sd_setImage(with: urlThumbnail, placeholderImage: UIImage(named: "placeholder.png"))
I'm tryin to write the datas of an Image in the cacheDirectory, like this :
let imageData = UIImageJPEGRepresentation(image, 1.0) else { fatalError("Impossible to read the image") }
try! imageData.write(to: cachedImageURL, options: .atomic)
print(fileManager.fileExists(atPath: cachedImageURL.absoluteString))
Where image is a working UIImage.
the Print statement always returns False. The file is never written, and any exception is raised due to the "try!"
Any idea ?
printing the cachedImageURL, it looks like this
file:///var/mobile/Containers/Data/Application/7B6B72C8-E794-4474-A658-48B66AEA31EC/Library/Caches/79d0e60e-5961-4538-bd9f-28187f4e26d0.jpg
Just figured out. The problem come from my print
print(fileManager.fileExists(atPath: cachedImageURL.absoluteString))
which should be cachedImageURL.path insteadOf cachedImageURL.absoluteString
print(fileManager.fileExists(atPath: cachedImageURL.path))
You can Try below Code for saving Images to Caches Directory
*NOTE : if you are saving more than one, please give different image name for all
let fileManager = FileManager.default
let imageName = "image.jpg" //Need to change if more images want to save
do {
let cachesDirectory = try fileManager.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor:nil, create:false)
let fileURL = cachesDirectory.appendingPathComponent(imageName)
let image = your image //Give your image here
if let imageData = UIImageJPEGRepresentation(image, 1.0) {
try imageData.write(to: fileURL)
print("Image Saved")
}
} catch {
print(error.localizedDescription)
}
I save my first image and it loads, but when I save the second, the image of the button stays the same as the first image I saved. Can someone show me how to have that files value be able to change?
Here is the code that runs when someone picks an image:
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// choose a name for your image
let fileName = "image.jpg"
// create the destination file url to save your image
let fileURL = documentsDirectory.appendingPathComponent(fileName)
// get your UIImage jpeg data representation and check if the destination file url already exists
if let data = UIImageJPEGRepresentation(pickedImage, 1.0),
!FileManager.default.fileExists(atPath: fileURL.path) {
do {
// writes the image data to disk
try data.write(to: fileURL)
print("file saved")
} catch {
print("error saving file:", error)
}
}
if let image = getSavedImage(named: "image.jpg") { Button1.setImage(image, for: .normal)
}
And here is my code for when the view loads:
if let image = getSavedImage(named: "image.jpg") {
Button1.setImage(image, for: .normal)
}
As #rmaddy mentioned you already saved image thats why image not replaced. but if you want on tap of button image will be change you need to do some extra work, you need to check if image.jpg exist then add new image with diff name.To check image exist use this method, This method return bool value and path of the file if file exist.So on these two values you can remove your existing image or create new image with different name, according to your code behaviour.
static func isFileExit(filename: String) -> (fileExist: Bool? , path: URL?) {
if let dir = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) {
let filepath = URL(fileURLWithPath: dir.absoluteString).appendingPathComponent(filename)
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filepath.path) {
print("FILEAVAILABLE")
return (true, filepath)
}
}
return (false,nil)
}
You're saving your images only if the image doesn't exist, so when you try to save the second image, its not being saved because the first image already exists.
//you should double check what you're doing here
if let data = UIImageJPEGRepresentation(pickedImage, 1.0),
!FileManager.default.fileExists(atPath: fileURL.path) {
do {
// writes the image data to disk
try data.write(to: fileURL)
print("file saved")
} catch {
print("error saving file:", error)
}
}
I would like to save images from a URL and then use them inside my app.
I saved them in variables but how can I make them persist until the user deletes the app ?
Here is the code for saving images in variables
let backgroundURL:NSURL? = NSURL(string: "http://i.imgur.com/4AiXzf8.jpg")
DispatchQueue.global(qos: .userInitiated).async {
let backgroundData:NSData? = NSData(contentsOf: backgroundURL as! URL)
DispatchQueue.main.async {
if (backgroundData != nil) {
background = UIImage(data: backgroundData! as Data
}
}
}
How can I save the background image to persist ?
Thank you!
For Swift 3
// Assuming background is UIImage
if let image = background {
if let data = UIImagePNGRepresentation(image) {
let filename = getDocumentsDirectory().appendingPathComponent("copy.png")
try? data.write(to: filename)
}
}
That call to getDocumentsDirectory()
func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
I would suggest you to store image in your document directory the below code you will be able to use after you downloaded image and converted as UIImage from NSData
Swift 2.3
let documentsDirectoryURL = try! NSFileManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
// Name of Image you want to store
let fileURL = documentsDirectoryURL.URLByAppendingPathComponent("ImageName.jpg")
if !NSFileManager.defaultManager().fileExistsAtPath(fileURL.path!) {
if UIImageJPEGRepresentation(image, 1.0)!.writeToFile(fileURL.path!, atomically: true) {
print("Image saved")
} else {
print("error saving Image")
}
} else {
print("Image name already exists")
}
And here is how you can get image
let fileManager = NSFileManager.defaultManager()
let imagePAth = (self.getDirectoryPath() as NSString).stringByAppendingPathComponent("imageName.jpg")
if fileManager.fileExistsAtPath(imagePAth){
let myImage: UIImage = UIImage(contentsOfFile: imagePAth)
}
else{
print("No Such Image")
}
// directoryPath is a URL from another VC
#IBAction func saveButtonTapped(sender: AnyObject) {
let directoryPath = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as NSURL
let urlString : NSURL = directoryPath.URLByAppendingPathComponent("Image1.png")
print("Image path : \(urlString)")
if !NSFileManager.defaultManager().fileExistsAtPath(directoryPath.absoluteString) {
UIImageJPEGRepresentation(self.image, 1.0)!.writeToFile(urlString.absoluteString, atomically: true)
displayImageAdded.text = "Image Added Successfully"
} else {
displayImageAdded.text = "Image Not Added"
print("image \(image))")
}
}
I am not getting any error but the Image is not getting saved in the document.
The problem there is that you are checking if the folder not exists but you should check if the file exists. Another issue in your code is that you need to use url.path instead of url.absoluteString. You are also saving a jpeg image using a "png" file extension. You should use "jpg".
edit/update:
Swift 4.2 or later
do {
// get the documents directory url
let documentsDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
print("documentsDirectory:", documentsDirectory.path)
// choose a name for your image
let fileName = "image.jpg"
// create the destination file url to save your image
let fileURL = documentsDirectory.appendingPathComponent(fileName)
// get your UIImage jpeg data representation and check if the destination file url already exists
if let data = image.jpegData(compressionQuality: 1),
!FileManager.default.fileExists(atPath: fileURL.path) {
// writes the image data to disk
try data.write(to: fileURL)
print("file saved")
}
} catch {
print("error:", error)
}
To write the image at the destination regardless if the image already exists or not you can use .atomic options, if you would like to avoid overwriting an existing image you can use withoutOverwriting instead:
try data.write(to: fileURL, options: [.atomic])
This is my answer for Swift 3, combining the 2 answers above:
let documentsDirectoryURL = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
// create a name for your image
let fileURL = documentsDirectoryURL.appendingPathComponent("Savedframe.png")
if !FileManager.default.fileExists(atPath: fileURL.path) {
do {
try UIImagePNGRepresentation(imageView.image!)!.write(to: fileURL)
print("Image Added Successfully")
} catch {
print(error)
}
} else {
print("Image Not Added")
}
An extension method in swift 4.2
import Foundation
import UIKit
extension UIImage {
func saveToDocuments(filename:String) {
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsDirectory.appendingPathComponent(filename)
if let data = self.jpegData(compressionQuality: 1.0) {
do {
try data.write(to: fileURL)
} catch {
print("error saving file to documents:", error)
}
}
}
}
#IBAction func saveButtonTapped(sender: AnyObject) {
let directoryPath = try! NSFileManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
let urlString : NSURL = directoryPath.URLByAppendingPathComponent("Image1.png")
print("Image path : \(urlString)")
if !NSFileManager.defaultManager().fileExistsAtPath(urlString.path!) {
UIImageJPEGRepresentation(self.image, 1.0)!.writeToFile(urlString.path! , atomically: true)
displayImageAdded.text = "Image Added Successfully"
} else {
displayImageAdded.text = "Image Not Added"
print("image \(image))")
}
}
Put the image in an NSData object; writing to a file with this class is a breeze, and it'll make the file size smaller.
By the way, I recommend NSPurgeableData. After saving the image, you can mark the object as purgeable, which will keep memory consumption. That may be a problem with your app, but might be with another you're crowding out.
In Swift 4.2 and Xcode 10.1
func saveImageInDocsDir() {
let image: UIImage? = yourImage//Here set your image
if !(image == nil) {
// get the documents directory url
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0] // Get documents folder
let dataPath = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("ImagesFolder").absoluteString //Set folder name
print(dataPath)
//Check is folder available or not, if not create
if !FileManager.default.fileExists(atPath: dataPath) {
try? FileManager.default.createDirectory(atPath: dataPath, withIntermediateDirectories: true, attributes: nil) //Create folder if not
}
// create the destination file url to save your image
let fileURL = URL(fileURLWithPath:dataPath).appendingPathComponent("imageName.jpg")//Your image name
print(fileURL)
// get your UIImage jpeg data representation
let data = UIImageJPEGRepresentation(image!, 1.0)//Set image quality here
do {
// writes the image data to disk
try data?.write(to: fileURL, options: .atomic)
} catch {
print("error:", error)
}
}
}
Although the answers are correct, I want to share utility functions for this purpose. You can use the following 2 methods to save and Image in Documents Directory and then load an image from Documents Directory. Here you can find the Detailed Article.
public static func saveImageInDocumentDirectory(image: UIImage, fileName: String) -> URL? {
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!;
let fileURL = documentsUrl.appendingPathComponent(fileName)
if let imageData = UIImagePNGRepresentation(image) {
try? imageData.write(to: fileURL, options: .atomic)
return fileURL
}
return nil
}
public static func loadImageFromDocumentDirectory(fileName: String) -> UIImage? {
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!;
let fileURL = documentsUrl.appendingPathComponent(fileName)
do {
let imageData = try Data(contentsOf: fileURL)
return UIImage(data: imageData)
} catch {}
return nil
}
Answer for Swift 5.x
func saveImageToDocumentsDirectory() {
let directoryPath = try! FileManager().url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let urlString : NSURL = directoryPath.appendingPathComponent("Image1.png") as NSURL
print("Image path : \(urlString)")
if !FileManager.default.fileExists(atPath: urlString.path!) {
do {
try self.image.jpegData(compressionQuality: 1.0)!.write(to: urlString as URL)
print ("Image Added Successfully")
} catch {
print ("Image Not added")
}
}
}
Note : image = your declared image.