I am using SSZipArchive to unzip files from a Url (http://www.colorado.edu/conflict/peace/download/peace.zip). For this I am using the function :
SSZipArchive.unzipFile(atPath: path, toDestination: documentsPath, progressHandler: {
Is it possible to extract files from Url using SSZipArchive ?
Is yes, how do I pass Url in atPath ?
You can't directly unzip without downloading the file from the URL.
This is how I do this,
func downLoad() {
let url : String = "http://www.colorado.edu/conflict/peace/download/peace.zip"
var localPath: NSURL?
Alamofire.download(.GET,url,
destination: { (temporaryURL, response) in
let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let pathComponent = response.suggestedFilename
localPath = directoryURL.URLByAppendingPathComponent(pathComponent!)
return localPath!
})
.response { (request, response, _, error) in
SwiftEventBus.post("DownloadedSuccessfully", sender: localPath!)
}
}
After downloading the content, unzip it where you want to.
SwiftEventBus.onMainThread(self, name:"DownloadedSuccessfully")
{
result in
let resultStatus = result.object as! NSURL
guard let zipPath: String = resultStatus.path! as String else {
return
}
guard let unZipPath = unzipPath() else {
return
}
let success = SSZipArchive.unzipFileAtPath(zipPath, toDestination: unZipPath)
print(unZipPath)
if !success {
return
}
}
I unzipped the file and stored it in the document directory
func unzipPath() -> String? {
let path = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let url = NSURL(fileURLWithPath: path)
do {
try NSFileManager.defaultManager().createDirectoryAtURL(url, withIntermediateDirectories: true, attributes: nil)
} catch {
return nil
}
if let path = url.path {
return path
}
return nil
}
From there you can access the data of the file. Here I used third party frameworks Alamofire and swiftEventBus. Hope this works for you.
The cache.db-wal file have the sensitive information in my application.Need to remove the cache files in Caches directory.
This should work for you.
let fileManager = NSFileManager.defaultManager()
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask).first! as NSURL
let documentsPath = documentsUrl.path
do {
if let documentPath = documentsPath
{
let fileNames = try fileManager.contentsOfDirectoryAtPath("\(documentPath)")
for fileName in fileNames {
if (fileName == "cache.db-wal")
{
let filePathName = "\(documentPath)/\(fileName)"
try fileManager.removeItemAtPath(filePathName)
}
}
let files = try fileManager.contentsOfDirectoryAtPath("\(documentPath)")
}
} catch {
print("Could not clear: \(error)")
}
In swift 3.1
** In my case i wanted to remove JSON data through the folder so i named it as "fsCachedData" you can change name accordingly..**
func removeCache() {
let caches = (NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0])
let appId = Bundle.main.infoDictionary!["CFBundleIdentifier"] as! String
let path = String(format:"%#/%#/Cache.db-wal",caches, appId)
do {
try FileManager.default.removeItem(atPath: path)
} catch {
print("ERROR DESCRIPTION: \(error)")
}
}
swift 4
func removeNetworkDictionaryCache() {
let caches = (NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0])
let appId = Bundle.main.infoDictionary!["CFBundleIdentifier"] as! String
let path = String(format:"%#/%#/Cache.db-wal",caches, appId)
do {
try FileManager.default.removeItem(atPath: path)
} catch {
print("ERROR DESCRIPTION: \(error)")
}
}
In Swift 5.2 solution (Just converted code)
func removeNetworkCache() {
let caches = (NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)[0])
let appId = Bundle.main.infoDictionary!["CFBundleIdentifier"] as! String
let path = String(format:"%#/%#/Cache.db-wal",caches, appId)
do {
try FileManager.default.removeItem(atPath: path)
} catch {
print("ERROR DESCRIPTION: \(error)")
}
}
I could find a solution for the above question.
func removeNetworkCache() {
let caches = (NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.CachesDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0])
let appId = NSBundle.mainBundle().infoDictionary!["CFBundleIdentifier"] as! String
let path = String(format:"%#/%#/Cache.db-wal",caches, appId)
do {
try NSFileManager.defaultManager().removeItemAtPath(path)
} catch {
print("ERROR DESCRIPTION: \(error)")
}
}
I am trying to use a file called Data.plist to store some simple unstructured data, and I placed this file at the root folder of my app. To make it simple to read/write to this file, I created the following DataManager struct. It can read Data.plist file with no problem, but it cannot write data to the file. I am not sure where the problem is, could anyone spot where might be wrong?
struct DataManager {
static var shared = DataManager()
var dataFilePath: String? {
return Bundle.main.path(forResource: "Data", ofType: "plist")
}
var dict: NSMutableDictionary? {
guard let filePath = self.dataFilePath else { return nil }
return NSMutableDictionary(contentsOfFile: filePath)
}
let fileManager = FileManager.default
fileprivate init() {
guard let path = dataFilePath else { return }
guard fileManager.fileExists(atPath: path) else {
fileManager.createFile(atPath: path, contents: nil, attributes: nil) // create the file
print("created Data.plist file successfully")
return
}
}
func save(_ value: Any, for key: String) -> Bool {
guard let dict = dict else { return false }
dict.setObject(value, forKey: key as NSCopying)
dict.write(toFile: dataFilePath!, atomically: true)
// confirm
let resultDict = NSMutableDictionary(contentsOfFile: dataFilePath!)
print("saving, dict: \(resultDict)") // I can see this is working
return true
}
func delete(key: String) -> Bool {
guard let dict = dict else { return false }
dict.removeObject(forKey: key)
return true
}
func retrieve(for key: String) -> Any? {
guard let dict = dict else { return false }
return dict.object(forKey: key)
}
}
You cannot modify the files inside your app bundle. So all the files that you get with Bundle.main.path(forResource:ofType:) are readable but not writable.
If you want to modify this file you will need to copy it inside your app's document directory first.
let initialFileURL = URL(fileURLWithPath: Bundle.main.path(forResource: "Data", ofType: "plist")!)
let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last!
let writableFileURL = documentDirectoryURL.appendingPathComponent("Data.plist", isDirectory: false)
do {
try FileManager.default.copyItem(at: initialFileURL, to: writableFileURL)
} catch {
print("Copying file failed with error : \(error)")
}
// You can modify the file at writableFileURL
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
}
}
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
}
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
}
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
}
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
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 :)
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)")
}
}
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.
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()
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)
})
}
}
You have to create a directory in the Documents directory to be able to store a file.
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
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)
}
}