I'm trying to store images inside the app's document folder, so the user can retrieve them at any later time that they want to. This is my code to store them:
func store(_ image: UIImage) -> String {
let imageName = "\(Date().timeIntervalSince1970)"
let imagePath = "\(documentasPath)/\(imageName).png"
let imageData = UIImagePNGRepresentation(image)
fileManager.createFile(atPath: imagePath, contents: imageData, attributes: nil)
return imagePath
}
And this is my code to retrieve the image from the storage:
func retrieveImage(from path: String) -> UIImage? {
guard fileManager.fileExists(atPath: path) else {
return nil
}
return UIImage(contentsOfFile: path)
}
It seems to work fine, except when I rebuild the app from xcode. Then all of my stored images disappear (although all of the paths I stored that pointed to them are still present and correct).
Is this some behavior of the default file manager? And is there a way to avoid this from happening? I want the images to only be deleted either manually or when I uninstall the app.
Thanks
The problem is that you are storing an absolute path. You can't do that, because your app is sandboxed, which means (in part) that the URL of the Documents folder can change. Store just the document name, and each time you want to save to it or write from it, calculate the path to the Documents folder again and append the document name and use that result as your path.
Change to this
func store(_ image: UIImage) -> String {
let imageName = "\(Date().timeIntervalSince1970)"
let documentsUrl = NSURL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
var imagePath = documentsUrl.appendingPathComponent("\(imageName).png")
let imageData = UIImagePNGRepresentation(image)
fileManager.createFile(atPath: imagePath, contents: imageData, attributes: nil)
return imagePath
}
func retrieveImage(from path: String) -> UIImage? {
guard fileManager.fileExists(atPath: path) else {
return nil
}
return UIImage(contentsOfFile: path)
}
Related
I'm using Realm and storing the file path to captured images as Strings. I want to retrieve them later for use in a tableView. Here's my code to store the path for each image:
func saveImage(imageName: String){
//create an instance of the FileManager
let fileManager = FileManager.default
//get the image path
thisImagePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(imageName)
//get the image taken with camera
let image = originalCapturedImage
//get the PNG data for this image
let data = UIImagePNGRepresentation(image!)
//store it in the document directory
fileManager.createFile(atPath: thisImagePath as String, contents: data, attributes: nil)
print("Picture path at assignment \n")
print(thisImagePath as Any)
}
And here's the code to retrieve the image:
...
var paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsPath = paths[0] //Get the docs directory
let filePath = URL(fileURLWithPath: documentsPath).appendingPathComponent(item.picPath).path
let image = UIImage(contentsOfFile: filePath)
print("Picture path at retrieval \n")
print(item.picPath as Any)
cell.imageWell.image = image
cell.imageWell.clipsToBounds = true
return cell
}
Here's the comparison of the file path at runtime:
Picture path at assignment
/var/mobile/Containers/Data/Application/0E9CACAD-C6B3-4F6C-B0DB-72C43AC722E1/Documents/1535219147
...
...
Picture path at retrieval
/var/mobile/Containers/Data/Application/0E9CACAD-C6B3-4F6C-B0DB-72C43AC722E1/Documents/1535219147
The paths appear identical to me, yet no image appears. I've searched all over SO, and at one point came across a mention of the use of the URL for the file path. Somehow, I lost track of that entry and haven't been able to find it again.
Any help would be greatly appreciated!
You can try with below code for writing the image to the file instead of creating file with content. try? data.write(to: URL(fileURLWithPath: documentsPath))
You can also refer below code for saving image.
class func saveImageToFileWithDirectory(_ imageData:Data, fileName:String, folderName : String? = nil) {
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))
} }
Code for retrieving the image looks good. If you need help in that also, pls comment, I will post that also.
Hope this solves your issue.
In my app I am storing an image in local storage and I am saving the path of that image in my Realm database. And now i have problems with load this image from that path?
Thats how I save path to database:
let nsDocumentDirectory = FileManager.SearchPathDirectory.documentDirectory
let nsUserDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(nsDocumentDirectory,nsUserDomainMask, true)
let dirPath = paths.first
let imageURL1 = URL(fileURLWithPath: dirPath!).appendingPathComponent("refuge1.jpg")
let med1 = Meditation()
med1.name = "Refuge"
med1.count = 0
med1.targetAmount = 111111
med1.malasStep = 108
med1.imagePath = imageURL1.path
med1.id = 1
It's straightforward that am trying to get an image from this meditation.imagePath path. I double-checked the path, image is there still am not able to set the image using this path, is there is something that am missing?
In debug mode I see this:
Meditation {
name = Refuge;
count = 0;
targetAmount = 111111;
malasStep = 108;
imagePath = /Users/macbook/Library/Developer/CoreSimulator/Devices/2E25309F-D6A9-41C3-9EF4-67203142172C/data/Containers/Data/Application/F198640B-3C72-4F9C-8173-FB00D3ABEC15/Documents/refuge1.jpg;
id = 1;}
but my variable image still nil in debug mode
// Configure the cell...
cell.nameOfMeditation.text = meditation.name
cell.countOfMeditation.text = String(meditation.count)
let image = UIImage(contentsOfFile: meditation.imagePath)
cell.imageForMeditation.image = image
return cell
I see name of meditation and sound, bot no omg.
It's not advised to save the absolute file path of a file in an iOS app (ie, everything including /Users/macbook/Library/Developer/...), in Realm or anywhere else.
For security reasons, iOS devices rename the UUID folder name between launches. This means that while the folder path was valid at the time it was saved, it won't be at a later date.
Instead, it's recommended to save just the relative path of the file (eg, its location in relation to just the Documents folder. In this case, it would be just /refuge1.jpg) and to then dynamically build the absolute path by requesting the Documents directory path as you need it.
Try this:
// Use these convenience methods if you do a lot of saving and loading
func getDocumentsURL() -> URL {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
return documentsURL
}
func fileInDocumentsDirectory(_ filename: String) -> String {
let fileURL = getDocumentsURL().appendingPathComponent(filename)
return fileURL.path
}
func saveRefugeOne(image: UIImage) {
// Create a file name, and then save the path in your documents with that name
let imageFileName:String = "refuge1.jpg"
let imagePath = fileInDocumentsDirectory(imageFileName!)
saveImage(image, path: imagePath)
}
func loadRefugeOne() -> UIImage? {
// Get the image back
let imageName:String = "refuge1.jpg" // Or whatever name you saved
let imagePath = fileInDocumentsDirectory(imageName)
if let loadedImage = self.loadImageFromPath(imagePath) {
return loadedImage
} else {
print("Couldn't Load: \(imageName)")
return nil
}
}
// This will be your method to save image
func saveImage(_ image: UIImage, path: String ) {
//If you want PNG use this let pngImageData = UIImagePNGRepresentation(image)
// But since you mentioned JPEG:
if let jpgData = UIImageJPEGRepresentation(image, 1.0) {
try? jpgData.write(to: URL(fileURLWithPath: path), options: [.atomic])
}
}
// This will load image from saved path. Make sure to store the path
// somewhere. This makes it easier to save images locally. You can
// save the image in the documents directory, and then just save the
// path in CoreData or something similar.
func loadImageFromPath(_ path: String) -> UIImage? {
let image = UIImage(contentsOfFile: path)
if image == nil {
print("couldn't find image at path: \(path)")
}
return image
}
Hopefully this will help. It's the method I always use, and it works like a charm when I follow my own steps right ;-)
I am writing an iOS app(in Swift) that includes and options to save pictures to a safe location. Once a picture is chosen(from Camera or Saved Images), it is written to the File and the file location is saved in NSUserDefaults. I am able to re-access the image, when I relaunch the app after killing it. But when I run the app from Xcode again(after recompiling), the image is lost from the Image path.
Attaching the saving and loading functions below
func getDocumentsURL() -> NSURL {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
return documentsURL
}
func fileInDocumentsDirectory(filename: String) -> String {
let fileURL = getDocumentsURL().URLByAppendingPathComponent(filename)
return fileURL.path!
}
func saveImage (image: UIImage, path: String ) -> Bool{
// let pngImageData = UIImagePNGRepresentation(image)
let jpgImageData = UIImageJPEGRepresentation(image, 1.0) // if you want to save as JPEG
let result = jpgImageData!.writeToFile(path, atomically: true)
return result
}
func loadImageFromPath(path: String) -> UIImage? {
let image = UIImage(contentsOfFile: path)
if image == nil {
print("missing image at: \(path)")
}
print("Loading image from path: \(path)") // this is just for you to see the path in case you want to go to the directory, using Finder.
return image
}
What could be causing this?
The Document Directory path is different after each run.
Save to NSUserDefaults only the relative path from the Documents Directory (what you append to the getDocumentsURL() NSURL).
So in your case, you don't specify a subdirectory in your apps Document Directory, so the only thing you need on the next run, is the file name that you saved.
After that, you can retrieve the saved image with:
func loadImageNamed(name: String) -> UIImage? {
let imagePath = fileInDocumentsDirectory(name)
let image = UIImage(contentsOfFile: imagePath)
if image == nil {
print("missing image at: \(path)")
}
print("Loading image from path: \(path)") // this is just for you to see the path in case you want to go to the directory, using Finder.
return image
}
When reinstalling the app, the documents directory will change. You shouldn't save the full path, but a relative path from the documents directory.
I am new to Swift and I am trying to save an image from the gallery of the phone (or the Internet) to my app. I found many tutorials showing how to view the image but not how to save it to my app (I guess to my assets folder) and use it later on. Can someone please pint me to the right direction?
func saveImage (image: UIImage, path: String ) -> Bool{
let pngImageData = UIImagePNGRepresentation(image)
//let jpgImageData = UIImageJPEGRepresentation(image, 1.0) // if you want to save as JPEG
let result = pngImageData!.writeToFile(path, atomically: true)
return result
}
Here is how to use the function later in your code:
saveImage(image, path: imagePath)
However before you can Save or Load anything you have to know the ‘path’ you are going to use.
We’ll need 3 things:
a function to pinpoint the documents directory
a file in the documents directory a function to pinpoint
a variable to store the location ( path ) of the image we want to save
Here is how we are going to do that:
func getDocumentsURL() -> NSURL {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
return documentsURL
}
func fileInDocumentsDirectory(filename: String) -> String {
let fileURL = getDocumentsURL().URLByAppendingPathComponent(filename)
return fileURL.path!
}
// Define the specific path, image name
let imagePath = fileInDocumentsDirectory(myImageName)
Visit for more: http://helpmecodeswift.com/image-manipulation/saving-loading-images
I have an ordinary Xcode project like this ...
notice there's a folder (it is an actual folder - not just a group) named "images". It contains 25 ".png" images.
All I want to do is make an array of UIimage with each of those images.
(Or even, an array of the image names or similar, that would be fine - then could load them UIImage(named:)
How do I get at that folder "images"?? What about a subfolder "images/cars"?
I tried something like this but it finds nothing...
override func viewDidLoad()
{
let imageArray = NSBundle.mainBundle().URLsForResourcesWithExtension(
"png", subdirectory: "images")
print("test...")
for n:NSURL in imageArray!
{ print("found ..." ,n) }
}
We we assume the images are in the app's resource bundle. If not you need to make sure that your images directory is listed in the "Copy Bundle Resources" in the "Build Phases" of the target.
EDIT
This is only going copy the images into the app bundle, if you require the folder to be copied to the app bundle per the code below then please use the follow StackOverflow question to set it up correctly.
This gives us an array of URL's that we can then use with UIImage(data:) and NSData(contentsOfURL:) to create the image when needed.
Get the bundle's resource path and append the image directory then get the contents of the directory.
if let path = NSBundle.mainBundle().resourcePath {
let imagePath = path + "/images"
let url = NSURL(fileURLWithPath: imagePath)
let fileManager = NSFileManager.defaultManager()
let properties = [NSURLLocalizedNameKey,
NSURLCreationDateKey, NSURLLocalizedTypeDescriptionKey]
do {
let imageURLs = try fileManager.contentsOfDirectoryAtURL(url, includingPropertiesForKeys: properties, options:NSDirectoryEnumerationOptions.SkipsHiddenFiles)
print("image URLs: \(imageURLs)")
// Create image from URL
var myImage = UIImage(data: NSData(contentsOfURL: imageURLs[0])!)
} catch let error1 as NSError {
print(error1.description)
}
}
You can follow these steps to download them:
Create a new folder in finder and add all images (or folder, ...
everything).
Change folder name + ".bundle" (for example: YourListImage ->
YourListImage.bundle).
Add folder to project.
Add FileManager extension:
extension FileManager {
func getListFileNameInBundle(bundlePath: String) -> [String] {
let fileManager = FileManager.default
let bundleURL = Bundle.main.bundleURL
let assetURL = bundleURL.appendingPathComponent(bundlePath)
do {
let contents = try fileManager.contentsOfDirectory(at: assetURL, includingPropertiesForKeys: [URLResourceKey.nameKey, URLResourceKey.isDirectoryKey], options: .skipsHiddenFiles)
return contents.map{$0.lastPathComponent}
}
catch {
return []
}
}
func getImageInBundle(bundlePath: String) -> UIImage? {
let bundleURL = Bundle.main.bundleURL
let assetURL = bundleURL.appendingPathComponent(bundlePath)
return UIImage.init(contentsOfFile: assetURL.relativePath)
}
}
Use:
let fm = FileManager.default
let listImageName = fm.getListFileNameInBundle(bundlePath: "YourListImage.bundle")
for imgName in listImageName {
let image = fm.getImageInBundle(bundlePath: "YourListImage.bundle/\(imgName)")
}
Please try the following:
You have to register your images to "Copy Bundle Resources".
You have to add filter module in main Bundle.
enter image description here
It is working well on my side. Maybe you can change filter from "jpg" format into "png" one.
I've tested on iOS 10.x later, Swift 3.0 and xcode 8.1 version.
I would advise against loading all of your images into an array at once. Images tend to be large and it's easy to run out of memory and crash.
Unless you absolutely have to have all the images in memory at once it's better to keep an array of paths or URLs and load the images one at a time as needed.
Assuming the folder full of images is in your app bundle, you can use the NSBundle method URLsForResourcesWithExtension:subdirectory: to get an array of NSURLs to all the files in your images subdirectory, either with a specific filetype, or ALL files (if you pass nil for the extension.)
Once you have an array of file urls you can map it to an array of paths if needed, and then map that to an array of images.
Swift 4
if let path = Bundle.main.resourcePath {
let imagePath = path + "/images"
let url = NSURL(fileURLWithPath: imagePath)
let fileManager = FileManager.default
let properties = [URLResourceKey.localizedNameKey,
URLResourceKey.creationDateKey,
URLResourceKey.localizedTypeDescriptionKey]
do {
let imageURLs = try fileManager.contentsOfDirectory(at: url as URL, includingPropertiesForKeys: properties, options:FileManager.DirectoryEnumerationOptions.skipsHiddenFiles)
print("image URLs: \(imageURLs)")
// Create image from URL
let firstImageURL = imageURLs[0]
let firstImageData = try Data(contentsOf: firstImageURL)
let firstImage = UIImage(data: firstImageData)
// Do something with first image
} catch let error as NSError {
print(error.description)
}
}