how to use writeToFile to save image in document directory? - ios

// 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.

Related

Saving video in locally (directory) in Swift?

I try to save given video locally after then I need those saved videos for playing video in my app. I can't handle the saving video. Here is my saving try :
func saveVideoDocumentDirectory(url : URL){
let fileManager = FileManager.default
let paths = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(".MOV")
do{
let videoData = try Data(contentsOf: url)
fileManager.createFile(atPath: paths as String, contents: videoData, attributes: nil)
}catch{
//
}
}
here is the get file try
func getVideo(){
let fileManager = FileManager.default
let videoPAth = (self.getDirectoryPath() as NSString).appendingPathComponent(".MOV")
if fileManager.fileExists(atPath: videoPAth){
print(videoPAth)
play(url: URL(string: videoPAth)!)
}else{
print("No Video")
}
}
here is my play video func :
func play(url : URL)
{
let player = AVPlayer(url: url)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
present(playerViewController, animated: true)
{
playerViewController.player!.play()
}
}
Instead of Filemanager.createFile(), try using write instead.
let videoData = try Data(contentsOf: url)
try videoData.write(to: paths, options: .atomic)
Also, I recommend creating a folder first (from this answer).
extension URL {
static func createFolder(folderName: String) -> URL? {
let fileManager = FileManager.default
// Get document directory for device, this should succeed
if let documentDirectory = fileManager.urls(for: .documentDirectory,
in: .userDomainMask).first {
// Construct a URL with desired folder name
let folderURL = documentDirectory.appendingPathComponent(folderName)
// If folder URL does not exist, create it
if !fileManager.fileExists(atPath: folderURL.path) {
do {
// Attempt to create folder
try fileManager.createDirectory(atPath: folderURL.path,
withIntermediateDirectories: true,
attributes: nil)
} catch {
// Creation failed. Print error & return nil
print(error.localizedDescription)
return nil
}
}
// Folder either exists, or was created. Return URL
return folderURL
}
// Will only be called if document directory not found
return nil
}
}
Then, you can save like this:
guard let folderURL = URL.createFolder(folderName: "StoredVideos") else {
print("Can't create url")
return
}
let permanentFileURL = folderURL.appendingPathComponent(nameOfYourFile).appendingPathExtension("MOV")
let videoData = try Data(contentsOf: url)
try videoData.write(to: permanentFileURL, options: .atomic)
This will save you the hassle of NSSearchPathForDirectoriesInDomains.

Can not get proper firebase image save in document directory in swift 4 iOS

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

Remove all files from within documentDirectory in Swift

I am making an audio app, and the user can download files locally stored to the documentDirectory using FileManager.
Next, I'd like to allow the user to delete all files using a button. In the documentation, there is a method to remove items.
Here's my code:
#IBAction func deleteDirectoryButton(_ sender: Any) {
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
do {
try FileManager.default.removeItem(at: documentsUrl, includingPropertiesForKeys: nil, options: [])
} catch let error {
print(error)
}
}
Unfortunately, this won't build with an error Ambiguous reference to member 'removeItem(atPath:)'.
Is there a better approach to access the documentDirectory and remove all files from the directory in one swoop?
First of all the error occurs because the signature of the API is wrong. It's just removeItem(at:) without the other parameters.
A second issue is that you are going to delete the Documents directory itself rather than the files in the directory which you are discouraged from doing that.
You have to get the contents of the directory and add a check for example to delete only MP3 files. A better solution would be to use a subfolder.
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
do {
let fileURLs = try FileManager.default.contentsOfDirectory(at: documentsUrl,
includingPropertiesForKeys: nil,
options: .skipsHiddenFiles)
for fileURL in fileURLs where fileURL.pathExtension == "mp3" {
try FileManager.default.removeItem(at: fileURL)
}
} catch { print(error) }
Side note: It is highly recommended to use always the URL related API of FileManager.
Try this
func clearAllFiles() {
let fileManager = FileManager.default
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
print("Directory: \(paths)")
do {
let fileName = try fileManager.contentsOfDirectory(atPath: paths)
for file in fileName {
// For each file in the directory, create full path and delete the file
let filePath = URL(fileURLWithPath: paths).appendingPathComponent(file).absoluteURL
try fileManager.removeItem(at: filePath)
}
} catch let error {
print(error)
}
}
Just use code as Follow
to save AudioFile in Document Directory as
func getDocumentsDirectory() -> URL
{
//Get Basic URL
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
/// Enter a Directory Name in which files will be saved
let dataPath1 = documentsDirectory.appendingPathComponent("folder_name_enter")
let dataPath = dataPath1.appendingPathComponent("folder inside directory if required (name)")
//Handler
do
{
try FileManager.default.createDirectory(atPath: dataPath.path, withIntermediateDirectories: true, attributes: nil)
}
catch let error as NSError
{
print("Error creating directory: \(error.localizedDescription)")
}
return dataPath
}
Delete
func clearAllFilesFromTempDirectory()
{
let fileManager = FileManager.default
let dirPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let tempDirPath = dirPath.appending("/folder_name/\(inside_directoryName)")
do {
let folderPath = tempDirPath
let paths = try fileManager.contentsOfDirectory(atPath: tempDirPath)
for path in paths
{
try fileManager.removeItem(atPath: "\(folderPath)/\(path)")
}
}
catch let error as NSError
{
print(error.localizedDescription)
}
}
Saving Method
getDocumentsDirectory().appendingPathComponent("\(audioName).wav")
Deletion Method
/// Just call
clearAllFilesFromTempDirectory
This my extension for remove all files and caches from directory.
// MARK: - FileManager extensions
extension FileManager {
/// Remove all files and caches from directory.
public static func removeAllFilesDirectory() {
let fileManager = FileManager()
let mainPaths = [
FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).map(\.path)[0],
FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).map(\.path)[0]
]
mainPaths.forEach { mainPath in
do {
let content = try fileManager.contentsOfDirectory(atPath: mainPath)
content.forEach { file in
do {
try fileManager.removeItem(atPath: URL(fileURLWithPath: mainPath).appendingPathComponent(file).path)
} catch {
// Crashlytics.crashlytics().record(error: error)
}
}
} catch {
// Crashlytics.crashlytics().record(error: error)
}
}
}
}
Swift 5
Delete the whole folder:
If you'd like to delete a whole folder you can simply do this:
func deleteFolder(_ folderName: String, completion: () -> Void) {
let fileManager = FileManager.default
let directory = fileManager.cachesDirectory().appendingPathComponent(folderName)
_ = try? fileManager.removeItem(at: directory)
completion()
}
Delete certain files based on their name:
This will loop through all the files and remove all that contain the
func removeFiles(containing: String, completion: () -> Void) {
let fileManager = FileManager.default
let directory = fileManager.cachesDirectory()
if let fileNames = try? fileManager.contentsOfDirectory(atPath: directory.path) {
for file in fileNames {
if file.contains(containing) {
let filePath = URL(fileURLWithPath: directory.path).appendingPathComponent(file).absoluteURL
_ = try? fileManager.removeItem(at: filePath)
}
}
}
completion()
}

Swift data write to Document directory maintaining directory structure

I am downloading file from firebase. let say the request url is following
social-cam-storage/albm-72/owner-2/1484043313786.jpeg
i can download the file using the following code
func downloadFile(url : String) {
let storageR = FIRStorage.storage().reference(withPath: url)
let maxSize : Int64 = 3 * 1024 * 1024 // 3MB
storageR.data(withMaxSize: maxSize) { (data, error) in
if error != nil {
print(error.debugDescription)
return
}
print(data!)
}
}
Now i need to store this data maintaining the directory structure of the url
I have tried
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
print(FileManager.default.createFile(atPath: "\(documentsURL.absoluteString)/\(url)", contents: data!, attributes: nil))
but i am getting false
so how to fix this or is there any other way to save??
Have you tried something like this? :
If you have the exact path already as a string:
try? data.write(to: URL(fileURLWithPath: path), options: [.atomic])
If you need the path there are a few methods:
func saveFile() {
let filePath = getDocumentsURL().absoluteString.appending(path)
try? data.write(to: URL(fileURLWithPath: filePath), options: [.atomic])
}
func getDocumentsURL() -> URL {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
return documentsURL
}
You could also just try saving the filename, and then loading later when you need it:
func fileInDocumentsDirectory(_ filename: String) -> String {
let fileURL = getDocumentsURL().appendingPathComponent(filename)
return fileURL.path
}
// To save file
func saveFile(data: Data) {
let fileName:String = "createUniqueFileName"
let filePath = fileInDocumentsDirectory(fileName)
saveData(data, filePath)
}
// To load file with saved file name
func loadFile(fileName: String) {
if let loadedData = loadData(fileName) {
// Handle data however you wish
}
}
func saveData(_ data: Data, path: String ) {
try? data.write(to: URL(fileURLWithPath: path), options: [.atomic])
}
func loadData(_ path: String) -> Data? {
let data:Data? = try? Data(contentsOf: URL(fileURLWithPath: path))
return data
}
Have you tried using the built in "download to file" API in Firebase Storage?
// Create a reference to the file you want to download
let fileURL = storage.reference(withPath: url)
// Create local filesystem URL
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = ...
// Download to the local filesystem
let downloadTask = islandRef.write(toFile: fileURL) { url, error in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Local file URL is returned
}
}

Save image from URL and make it persist IOS Swift

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

Resources