I am sending some images via socket and I would like to create text file with information about the images to send over the network as well. I right now I can send the images no problem by creating a variable for the image data like so
let imageData = UIImageJPEGRepresentation(someUIImage, 1.0)
How do create a variable with the data of the text file?
let textData = someTextFileAsData.....
Is this what you want?
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
// Get the URL to the file. Below is an example
let fileURL = documentsDirectory.appendingPathComponent("test").appendingPathExtension("txt") // Replace "test" with your fileName and "txt" with your fileExtension
var text = ""
do {
text = try String(contentsOf: fileURL)
} catch {
fatalError("error: \(error.localizedDescription)")
}
Expanding a bit on Anthonin C.'s answer:
Each file in iOS is identified by a URL (just like a webpage, but this is a URL referring to the iOS filesystem). There are a few places in the system where you can save files, most of which you can get the URLs for by reading
FileManager.default.urls(for: SearchPathDirectory, in: SearchPathDomainMask)
(FileManager class reference).
For instance, to save in the user's personal "documents" directory, you would do:
let fileName = "socketLog.txt"
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
//build full path to file
let path = dir.appendingPathComponent(fileName)
do {
try text.write(to: path, atomically: false, encoding: String.Encoding.utf8)
}
catch {/* error handling here */}
}
where text would be the string data you want to save.
You should use write(to: URL, atomically: Bool) or write(toFile: String, atomically: Bool) of NSData method to write your data in a file.
In your case it would be :
let imageData = UIImageJPEGRepresentation(someUIImage, 1.0)
imageData.writeToFile("imageData.txt", atomically:true)
You can then restore it like that :
var imageData = NSData(contentsOfFile: "imageData.txt")
And to get back the image from data :
let image : UIImage = UIImage(data: imageData)
Related
I'm working on an iOS application. In the app, I am using Alamofire to create a POST request that returns a raw PDF file in response. Right now, I am able to save the file and open it with UIDocumentInteractionController. But, I want the file to stay in User's documents folder.
Here's how I create the destination path:
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent("Dividend Summary Report.pdf")
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
Someone please tell me what's wrong with my logic and what I can do to correct it.
Well you need to check the file status if it exists then read from documentDirectory else download the file.
Create function like this:
func checkIfFileExists(urlString: String) {
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let fileName = urlString
let fileManager = FileManager.default
let filePath = url.appendingPathComponent("\(fileName).pdf")?.path
print("filePath : \(String(describing: filePath))")
if fileManager.fileExists(atPath: filePath!) {
print("File exists")
} else {
print("File doesn't exists")
// set your download function here
}
}
I have a data.json file in the root of my Swift 5 project to which I would like to write some data. The file is already filled with data beforehand, but I would like to overwrite it.
This is the function I use to encode an array of Task structs into JSON Data:
func encode(task: Task){
loadJSON()
t.append(task)
if let json = try? JSONEncoder().encode(t){
saveJSON(json: json)
}
}
loadJSON() loads the JSON data into the t array from the data.json file, then the new Task is appended to the array, and then I create a json constant with encoded data from the t array, and call the saveJSON function.
func saveJSON(json: Data){
if let path = Bundle.main.path(forResource: "data", ofType: "json"){
let url = URL(fileURLWithPath: path)
do{
try json.write(to: url, options: .atomicWrite)
}
catch{
print(error)
}
}
}
The path and the url are set, then the do is executed and it just falls through without doing anything. The data.json file is unchanged and json.write doesn't throw any errors.
I'm not entirely sure why am I unable to write to the file. I've tried to write a simple string instead of a data set, with similar results.
The problem seems to be caused by your attempting to write to the bundle directory, which cannot be written to directly (from all accounts, it seems). Moving your file to a writable directory, like the user's documents directory, will allow you to modify or delete the file as you wish.
You can copy your resource file into the documents directory so it can be accessed locally, and made available to the user as well. Here's an example helper method that copies a file (in your case, pass the string data.json as the sourceFile argument) from the Bundle into the documents directory:
func copyFileFromBundleToDocumentsFolder(sourceFile: String, destinationFile: String = "") {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
if let documentsURL = documentsURL {
let sourceURL = Bundle.main.bundleURL.appendingPathComponent(sourceFile)
// Use the same filename if destination filename is not specified
let destURL = documentsURL.appendingPathComponent(!destinationFile.isEmpty ? destinationFile : sourceFile)
do {
try FileManager.default.removeItem(at: destURL)
print("Removed existing file at destination")
} catch (let error) {
print(error)
}
do {
try FileManager.default.copyItem(at: sourceURL, to: destURL)
print("\(sourceFile) was copied successfully.")
} catch (let error) {
print(error)
}
}
}
After doing that -- if you need to make modifications to the newly created file, you can create another function that overwrites the file with your specific json data using almost exactly the same logic.
You can adapt your loadJSON() and saveJSON() functions to read from or write to this new file (inside the document directory). For saving, something like this:
func saveJSONDataToFile(json: Data, fileName: String) {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
if let documentsURL = documentsURL {
let fileURL = documentsURL.appendingPathComponent(fileName)
do {
try json.write(to: fileURL, options: .atomicWrite)
} catch {
print(error)
}
}
}
EDIT:
Changes to the Document Directory should persist. Here is code to retrieve the list of all files in that directory. It should help to check that the file in question is being persisted properly:
func listDocumentDirectoryFiles() {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
if let url = documentsURL {
do {
let contents = try FileManager.default.contentsOfDirectory(atPath: url.path)
print("\(contents.count) files inside the document directory:")
for file in contents {
print(file)
}
} catch {
print("Could not retrieve contents of the document directory.")
}
}
}
Hope this helps. Feel free to ask in the comments if anything is unclear. Good luck!
Below is my code -
I have tried to get the document directory path and with standard FileManager singleton tried to create a file, but I am not able to create the file, as the error -
Unable to store data: Error Domain=NSCocoaErrorDomain Code=4 "The file “CrashLog.txt” doesn’t exist."
UserInfo={NSFilePath=file:///Users/ABC/Library/Developer/CoreSimulator/Devices/87317777-63E7-422B-A55F-878E3267AFB8/data/Containers/Data/Application/4B41AA87-E4B9-4EE4-A67F-AC3B018913CC/Documents/CrashLog,
NSUnderlyingError=0x600000244ec0 {Error Domain=NSPOSIXErrorDomain
Code=2 "No such file or directory"}}
Code in development -
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
if (paths.count > 0) {
let documentsDirectory = paths[0]
let logFilePath = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("CrashLog.txt").absoluteString
let _string = "Hello"
//Create file at given path
let data = _string.data(using: .utf8)
//let attributes = FileManager.default.attributesOfItem(atPath: logFilePath)
let fileExists : Bool = FileManager.default.fileExists(atPath: logFilePath)
print(fileExists)
let isFileCreated = FileManager.default.createFile(atPath: logFilePath, contents: data, attributes: nil)
print("ifFileCreated", isFileCreated)
}
Here's my take on what you've done. Adopt the URL-based means of working with files. The best way to write data (for this example, at least), is to use Data's ability (not FileManager) to write to a file, again, using a URL. In most cases, you don't need to worry whether the file exists or not; just do it, and handle any error that arises.
if var url = try? FileManager.default.url(for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false) {
url = url.appendingPathComponent("CrashLog").appendingPathExtension("txt")
let _string = "Hello"
if let data = _string.data(using: .utf8) {
do {
try data.write(to: url)
print("successful")
} catch {
print("unsuccessful")
}
}
}
The appendingPathComponent method if the receiver (e.g. parameter) does not end with a trailing slash, then it may read file metadata to determine whether the resulting path is a directory. That means it may produce the error you are seeing, so better use the appendingPathComponent(_:isDirectory:) instead.
For example:
let logFilePath = URL(fileURLWithPath: documentsDirectory).appendingPathComponent("CrashLog.txt", isDirectory: false).absoluteString
The API absoluteString is wrong. The correct API is path
absoluteString returns the entire URL string representation including the scheme file://. On the other hand the path API of FileManager expects file system paths, the string without the scheme.
You are encouraged to use the URL related API anyway and you can write Data directly to disk without explicitly creating a file.
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let logFileURL = documentsURL.appendingPathComponent("CrashLog.txt")
let string = "Hello"
let data = Data(string.utf8)
let fileExists = FileManager.default.fileExists(atPath: logFileURL.path)
print(fileExists)
do {
try data.write(to: logFileURL)
print("data written")
} catch { print(error) }
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)
}
}
I am developing some tumblr client. And i want to download all photos in dashboard and save them to documentDirectory. Some of them is jpg some are gif. I have successfully download jpg images with this code:
do {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsURL.appendingPathComponent(fname)
if let pngImageData = UIImagePNGRepresentation(myImage!) {
try pngImageData.write(to: fileURL, options: .atomic)
print(fname + " - saved")
}
} catch {print(fname + " - not saved") }
myImage is the image which is downloaded from URL. This code is work for jpg images but not good for gifs.
And lastly i read these files with this:
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let filePath = documentsURL.appendingPathComponent(fname).path
if FileManager.default.fileExists(atPath: filePath) {
return UIImage(contentsOfFile: filePath)!
}
It's work for jpg but not for gifs. when i try this writing/reading codes on gifs images, i got just image not animation.
Can you show me the way pls with an example will be awesome.
Thank you.
I personally used this tinnie tool for GIFs. Simply add the iOSDevCenters+GIF.swift file directly to your project and use it as shown below
1. Online URL
let imageURL = UIImage.gifImageWithURL(gifURL)
let yourImageView = UIImageView(image: imageURL)
2. Local Filename
let imageName = UIImage.gifImageWithName(imageName)
let yourImageView = UIImageView(image: imageName)
3. Using Data
let imageData = NSData(contentsOf: Bundle.main.url(forResource: "name", withExtension: ".gif"))
let imageData = UIImage.gifImageWithData(imageData)
let yourImageView = UIImageView(image: imageData)
Actually your first code is not working correctly either.
What you are receiving is the image. You do not need or want to translate it into some other kind of image (UIImagePNGRepresentation). Just save the data. Later, read the data directly into a UIImage.
I think there is no problem with downloading and you are just trying to present it as a png; instead try to present this saved data with this library, for example
Try this:
#import AssetsLibrary
let lib = ALAssetsLibrary()
let data = NSData(contentsOfURL: getCurrentGIFURL)
lib.writeImageDataToSavedPhotosAlbum(data, metadata: nil) { (url, err) in
}