How to save NSData as gif to Album - ios

I use function here https://gist.github.com/westerlund/eae8ec71cdac88be7c3a
to create a gif file of an array of images, but the return type is NSData.
How can I use this data and save it to album?
I have tried UIImageWriteToSavedPhotosAlbum. It's not work, it only saved the first image of the image array. I have googled a function named writeimagedatatosavedphotosalbum, but it's already been deprecated in iOS9.
So what's the latest way the save a .gif file to album?

First Get a file URL
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileURL = documentsDirectory.appendingPathComponent("myGifFile.gif")
Write gifData to File URL
try? gifData.write(to: fileURL)
Save the file from URL to Album
PHPhotoLibrary.shared().performChanges ({
PHAssetChangeRequest.creationRequestForAssetFromImage(atFileURL: fileURL)
}) { saved, error in
if saved {
print("Your image was successfully saved")
}
}

Related

When exporting core data to CSV, how do I also save an image?

I have an entity named Item. I have an export function that exports all Items to CSV. So name, weight, quantity and etc is all exported correctly. The purpose of this is to save the data so that it may be imported later if all the data was deleted. One of the attributes of Items is a picture that the user chooses from its own library. How do I export that picture, so that it can be reimported later?
This is on iOS using the latest swift and Xcode.
I know I have not included any code, I am mainly asking for a direction to look. I'm not sure if I can get the location of the image on the device and then save that to the CSV or if there's a similar way. Thank you!
So I solved this problem using the code below but I may have created a new one. I'll be posting a new question to better clarify
The code below allowed me to save to Documents which allowed me to export and import the images.
func saveImage(image: UIImage, string: String) -> 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 {
print(string)
try data.write(to: directory.appendingPathComponent(string)!)
print("Success - \(string)")
return true
} catch {
print(error.localizedDescription)
return false
}
}
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
}
CSV is a text based file format but Images are binary data. So the two does not mix well.
One thing you can do is convert the image to Base64 String and insert that string to the CSV. But the string would be too large and there may be consequences.
If you are using the same device (probably not) you can get the path of the image and append it to the CSV.
If you are using a DB simply upload your images to it and add the path to the CSV. (you can even upload the images to your drive and add the path)
There may be other ways also.

Saving CloudKit Record to Local File Saves all fields Except CKAsset

I am trying to save an array of CKRecords to the documents directory in
order to have fast startup and offline access.
Downloading the CKRecords from CloudKit works fine and I am able to use the CKAsset in each record without issue. However, when I save the array of CKRecords that I downloaded to a local file, the CKAsset is not included in the data file. I can tell this from the size of the file saved to the documents directory. If I reconstitute the disk file into an array of CKRecords, I can retrieve all of the fields except the CKAsset. Other than the system fields, and the CKAsset field, all of the fields are Strings.
For testing - I have 10 CloudKit records each with six small String fields
and a CKAsset which is about 500KB. When I check the size of the
resulting file in documents the file size is about 15KB.
Here's the function to save the array. AppDelegate.ckStyleRecords is a
static array of the downloaded CKRecords.
func saveCKStyleRecordsToDisk() {
if AppDelegate.ckStyleRecords.count != 0 {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docsDirectoryURL = urls[0]
let ckStyleURL = docsDirectoryURL.appendingPathComponent("ckstylerecords.data")
do {
let data : Data = try NSKeyedArchiver.archivedData(withRootObject: AppDelegate.ckStyleRecords, requiringSecureCoding: true)
try data.write(to: ckStyleURL, options: .atomic)
print("data write ckStyleRecords successful")
} catch {
print("could not save ckStyleRecords to documents directory")
}
}//if count not 0
}//saveCKStyleRecordsToDisk
Here is the function to reconstitute the array.
func checkForExistenceOfCKStyleRecordsInDocuments(completion: #escaping ([CKRecord]) -> Void) {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docsDirectoryURL = urls[0]
let ckStyleURL = docsDirectoryURL.appendingPathComponent("ckstylerecords.data")
var newRecords : [CKRecord] = []
if FileManager.default.fileExists(atPath: ckStyleURL.path) {
do {
let data = try Data(contentsOf:ckStyleURL)
//yes, I know this has been deprecated, but I can't seem to get the new format to work
if let theRecords: [CKRecord] = try NSKeyedUnarchiver.unarchiveObject(with: data) as? [CKRecord] {
newRecords = theRecords
print("newRecords.count is \(newRecords.count)")
}
} catch {
print("could not retrieve ckStyleRecords from documents directory")
}
}//if exists
completion(newRecords)
}//checkForExistenceOfckStyleRecordsInDocuments
Calling the above:
kAppDelegate.checkForExistenceOfCKStyleRecordsInDocuments { (records) in
print("in button press and records.count is \(records.count)")
//this is just for test
for record in records {
print(record.recordID.recordName)
}
AppDelegate.ckStyleRecords = records
}//completion block
Upon refreshing the tableView that uses the ckStyleRecords array, all data
seems correct except the CKAsset (which in this case is a SceneKit
scene) is of course missing.
Any guidance would be appreciated.
A CKAsset was just a file reference. the fileURL property of the CKAsset is where the actual file is located. If you save a SKAsset then you only save the reference to the file. When doing that you do have to remember that this url is on a cache location which could be cleared if you are low on space.
You could do 2 things.
1. when reading your backup CKAsset, then also check if the file is located at the fileURL location. If the file is not there, then read it again from CloudKit.
2. Also backup the file from the fileURl to your documents folder. When you read your CKAsset from your backup, then just don't read the file from fileURL but the location where you have put it in your documents filter.

iOS / Swift failing to write file as treating file path as directory path [duplicate]

This question already has an answer here:
UIImage(contentsOfFile:) returning nil despite file existing in caches directory [duplicate]
(1 answer)
Closed 4 years ago.
I have the following swift function that I hoped would save incoming bytes to a JPEG file on iOS. Unfortunately an exception is thrown by the call to data.write and I get the error message
The folder “studioframe0.jpg” doesn’t exist. writing to file:/var/mobile/Containers/Data/Application/2A504F84-E8B7-42F8-B8C3-3D0A53C1E11A/Documents/studioframe0.jpg -- file:///
Why does iOS think it is a directory path to a directory which does not exist as opposed to a file that I am asking it to write?
func saveToFile(data: Data){
if savedImageCount < 10 {
guard let documentDirectoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
let imgPath = URL(fileURLWithPath: documentDirectoryPath.appendingPathComponent("studioframe\(savedImageCount).jpg").absoluteString)
savedImageCount += 1
do {
try data.write(to: imgPath, options: .atomic)
print("Saved \(imgPath) to disk")
} catch let error {
print("\(error.localizedDescription) writing to \(imgPath)")
}
}
}
URL(fileURLWithPath together with absoluteString is wrong.
You would have to write (note the different URL initializer):
let imgPath = URL(string: documentDirectoryPath.appendingPathComponent("studioframe\(savedImageCount).jpg").absoluteString)
but this (URL → String → URL) is very cumbersome, there is a much simpler solution, please consider the difference between (string) path and URL
let documentDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! // the Documents directory is guaranteed to exist.
let imgURL = documentDirectoryURL.appendingPathComponent("studioframe\(savedImageCount).jpg")
...
try data.write(to: imgURL, options: .atomic)

iOS - FileManager won't delete json file in document directory

I created a file in the document directory and for some reason when I try to delete it using the code below, it doesn't get deleted. The code doesn't throw any errors, but the file is still there
let fileManager = FileManager.default
if let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
let filePath = documentDirectory.appendingPathComponent("data.json")
do {
try fileManager.removeItem(atPath: filePath.path)
} catch let error as NSError {
print(error.localizedDescription)
}
}
I also tried to put it inside another folder and remove the folder but still the same problem.
This is the path where the file is stored :
Users/user1/Library/Developer/CoreSimulator/Devices/76AFDB69-75C8-464E-93F2-6ABF622068FD/data/Containers/Data/Application/7D268156-977A-4A3C-834B-6B13FA3DE76D/Documents/
you can try like below
if let filePath = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("data.json"), fileManager.fileExists(atPath: filePath.path) {
do {
try fileManager.removeItem(atPath: filePath.path)
} catch let error as NSError {
print(error.localizedDescription)
}
}
The code doesn't throw any errors, but the file is still there
No, it isn't. The problem is with the way you are checking to see whether "the file is still there". You are looking on your computer for the file. But iOS files are sandboxed. At the time you are looking, the path where the file was is completely different from your claimed path:
Users/user1/Library/Developer/CoreSimulator/Devices/76AFDB69-75C8-464E-93F2-6ABF622068FD/data/Containers/Data/Application/7D268156-977A-4A3C-834B-6B13FA3DE76D/Documents/
Such paths are not permanent. They are meaningless and should not be used. The only way to know whether the file is still there is with more code from inside iOS, i.e. ask the FileManager. When you do, you will find that you are, indeed, deleting the file successfully.

loading data object from local file in Swift 3

I'm struggling to get an image file loaded in Swift 3.
Here is the code:
do {
let imageData = try Data(contentsOf: imageUrl2.asURL())
} catch {
print ("loading image file error")
}
And the current Url String is:
file:///Users/veikoherne/Library/Developer/CoreSimulator/Devices/889A08D5-B8CC-458C-99FF-643A4BA1A806/data/Containers/Data/Application/F64ED326-7894-4EE7-AA3B-B1BB10DF8259/Documents/img2017-03-23 17:39:24.jpg
and obviously I have checked that this file exists and is valid image. It always ends up telling me "loading image file error". Anyone have experiences loading local data in Swift 3?
The answer mentioned was using NSData object and probably Swift 2. Current Swift 3 refuses to bridge NSData to Data, that's why I have to use Data.
Loading data from local file you should use "contentsOfFile:" method.
Reference link: https://www.hackingwithswift.com/example-code/strings/how-to-load-a-string-from-a-file-in-your-bundle
So in case of reading data you can use:
Data(contentsOf: <URL>, options: <Data.ReadingOptions>)
Reading a plain text as a String, use:
String(contentsOfFile: <LocalFileDirPath>)
Reading an image from document directory, use:
UIImage(contentsOfFile: <LocalFileDirPath>)
Hope this would be helpful!
I experienced the same issue when trying to retrieve a file that I just downloaded. If you have saved a file from some url like I did, this should work:
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let localUrl = documentDirectory.appendingPathComponent("somefile.txt")
if FileManager.default.fileExists(atPath: localUrl.path){
if let cert = NSData(contentsOfFile: localUrl.path) {
return cert as Data
}
}
Swift 5 version.
func loadFileFromLocalPath(_ localFilePath: String) ->Data? {
return try? Data(contentsOf: URL(fileURLWithPath: localFilePath))
}

Resources