Delete File After contentsOfFile Load - ios

I am loading an array of UIImage from the iOS Documents directory:
var images = [UIImage]()
for fileName in fileNames {
images.append(UIImage(contentsOfFile: "\(imagesPath)/\(fileName).png")!)
}
I'm going to continue using this array but I don't need the files anymore, so I go ahead and delete them:
for fileName in fileNames {
do {
try NSFileManager.defaultManager().removeItemAtPath("\(imagesPath)/\(fileName).png")
} catch {
print("Error")
}
}
When I do this, my array of UIImage is now invalid and gives me errors while trying to access them. Shouldn't this be in memory and not related to the files on disk?
I tried using the ".copy()" command on the images when I load them but that made no difference.
I have confirmed that the delete is the issue above because if I comment out that line the app works great with no errors. I only get errors accessing the array after I delete the files from disk.
Is there a way to sever this connection?
Edit: Per the correct answer from #Wain, the code works fine if I change it to:
var images = [UIImage]()
for fileName in fileNames {
let imgData = NSFileManager.defaultManager().contentsAtPath("\(imagesPath)/\(fileName).png")!
images.append(UIImage(data: imgData)!)
}
Doing this doesn't keep the link back to the file on disk.

The images haven't been displayed so the data hasn't fully been loaded yet, only enough to know the image details and size has been loaded. This is for memory efficiency. depending on the image format and usage different parts of data may be loaded at multiple different times.
If you load the file into NSData, ensuring that it isn't memory mapped, and create the image from that then the data and image should be unlinked from the underlying file. This is less memory efficient and it would be better to keep your current code and delete the files when you know you're finished with the images all together.

Related

Reduce base64 encoded string size

I am using the following method to save the PNG image to UserDefaults:
func convertImageToBase64String (img: UIImage) -> String {
return img.pngData()?.base64EncodedString() ?? ""
}
The issues are that this data is using significantly Documents & Data storage:
And for some reason even if this data is removed from UserDefaults, this amount do not change.
I would like to fix this issues, but need to use PNG images as transparent background is important.
After some research, I found out PNG files were not using significant storage. The issue was that original images were being stored in temporary data. I managed to fix this issue following this suggestion: https://stackoverflow.com/a/37043842/14880207
I followed Ramesh Sanghar suggestion as well and stored images in the way he suggested.

How to check the size of CoreData in Swift

I am storing data in CoreData. I want to know what is the size of whole CoreData in MBs. Basically the requirement is to clear the whole CoreData once it reaches 10 MB. I did not find any working answers for this. I have tried using below code but unable to get the size.
public func getSqliteStoreSize(forPersistentContainerUrl storeUrl: URL) -> String {
do {
let size = try Data(contentsOf: storeUrl)
if size.count < 1 {
print("Size could not be determined.")
return ""
}
let bcf = ByteCountFormatter()
bcf.allowedUnits = [.useMB] // optional: restricts the units to MB only
bcf.countStyle = .file
let string = bcf.string(fromByteCount: Int64(size.count))
print(string)
return string
} catch {
print("Failed to get size of store: \(error)")
return ""
}
}
guard let storeUrl = self.managedObjectContext!.persistentStoreCoordinator!.persistentStores.first?.url else {
print("There is no store url")
return
}
print("The size of the store is: \(self.getSqliteStoreSize(forPersistentContainerUrl: storeUrl))")
Reference: How to get the size of data present in coredata store?
Below is my coredata path.
file:///var/mobile/Containers/Data/Application/XXXXXX-XXX-XXXX-XXXX-XXXXXXXXXX/Library/Application Support/APPNAME.sqlite
Part of the answer is that you need to count the SQLite journal files. That's where most of the data actually lives. Getting a very low number is normal if you only count the central SQLite file. You find those by adding .-wal and -shm to file file name.
Another issue with your code is that using Data(contentsOf:) is a really bad idea here unless your store files are really small. You're reading the entire file into memory to find out how big it is, and that's not needed. The question you linked to has an answer showing how to use FileManager (actually NSFileManager there since it's ObjC but the function names and arguments are the same) to get the size without reading the file.
A final problem which might or might not affect you is that if you have external binary support turned on for any attribute in your model, the external files that get created are not in any of the SQLite files (that's the point). You also need to count those. The location is undocumented, but it's a directory named something like .APPNAME_SUPPORT/_EXTERNAL_DATA/. If you need this, try using a simulator build so that you can easily browse the app's files and make sure you have it right. You'd count every file in that folder.
By default, CoreData uses SQLite’s WAL mode (https://www.sqlite.org/wal.html) so SQLite creates 3 files for the database: the usual .sqlite file, a .sqlite-wal file, and a .sqlite-shm file. You will need to combine the sizes for all these.

How to generate smaller url bookmarks in Swift?

I want to create bookmarks for locally stored files so that the files can be tracked if they are moved to new locations.
My attempts to use Swift 3's NSURL function bookmarkData(options:includingResourceValuesForKeys:relativeTo) finds it obligatorily generating bookmark data objects that are almost a megabyte in size, even in cases in which the files themselves are orders of magnitude smaller than that.
For example, for a 4-byte text file, the following code produces 410kb bookmark data.
(Running in AppDelegate's applicationDidFinishLaunching, OS X, Xcode 8.2)
(Note that when I tried using NSURL.BookmarkCreationOptions.minimalBookmark, instead of .suitableForBookmarkFile, the output was only 0kb and I could not use it to track the original file.)
Similarly, bookmarking a 19kb .jpg file produces an 888kb bookmark.
Am I doing something wrong? If not, is there a way to get bookmarks that is not quite so greedy?
let url = URL(fileURLWithPath: "/Users/U/test.txt", isDirectory: false)
if let urlBookmark = try? url.bookmarkData(options: .suitableForBookmarkFile, includingResourceValuesForKeys: nil, relativeTo: nil) {
print("bookmark size = \(urlBookmark.count / 1024) kb")
}
Edit:
From a few further quick and dirty tests, I see that the size of the bookmark is fixed and is determined by the file's category rather than by the file's size:
.pages 479kb
.txt 419kb
.pdf 898kb
.numbers 309kb
.jpg 888kb
.png 915kb
.exe 342kb
.mp4 691kb
.mkv 594kb
Still, I would like to know if there is any way within those constraints to get even smaller bookmarks.

how to read data in rich content files using NSFilehandler

This is my swift code simplified:
func readContent(url:NSURL!){
do {
let file: NSFileHandle? = try NSFileHandle(forReadingFromURL: url)
if (file != nil){
file?.seekToFileOffset(10)
let content=file!.readDataOfLength(5)
print(String(data: content, encoding: NSASCIIStringEncoding))
file!.closeFile()
}
}catch {
}
}
I am building a file reader, that could read text files and pdfs. I am using NSFileHandler because I need to keep track of the position where the reading is happening.
I am able to read text files without a problem. However with pdfs I am having two problems:
How do I get the type of encoding used to encore the pdf? NSASCIIStringEncoding for instance does not work fine with text file but not with the pdf file. I am getting strange characters. I imaging that there is a way to detect the encoding. I have been following https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/Introduction/Introduction.html and I haven't found anything addressing this issue on stack overflow.
Given the fact that pdfs may contain text, images and videos how do I identify these contents while reading.. I read that magic numbers might do it https://en.wikipedia.org/wiki/Magic_number_(programming)#Magic_numbers_in_files but I read that there are not advisable http://www.techrepublic.com/article/avoid-using-magic-numbers-and-string-literals-in-your-code/. In addition I have not yet found a guide on how to use them.
please note that it is important for me to keep track of the Offset while reading.

Does NSFileWrapper load everything into memory?

Lets say I have an NSFileWrapper directory. This directory is made up of several levels of directories and files. Some of the files are large. Are all these files loaded into memory, or are they lazily loaded?
If they are loaded into memory, are there any alternatives to NSFileWrapper with similar functionality that won't load files into memory? Something I can hook into UIDocument?
This is for a document based application, which uses UIDocument's that are synced with iCloud. A document can have images and videos embedded inside it. Each image/video has a preview image (thumbnail) that is shown in an HTML document. The full size images or videos shouldn't be loaded into memory, but rather loaded on demand.
I also need a way to add a resource without loading it into memory. Something like "initWithAsset:(ALAsset *)" would be ideal.
I've made an app a while ago that generates a video. This video was then saved to a specific file format using a UIDocument subclass.
The only way to make the app not run out of memory while executing contentsForType:error: was to output the video to a file in the tmp dir and init the filewrapper with the url to the video with NSFileWrapperReadingWithoutMapping-option to prevent it from loading the video to memory and just copy in the file.
- (id)contentsForType:(NSString *)typeName error:(NSError **)outError {
if (self.fileWrapper == nil) {
self.fileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil];
}
if (self.videoURL != nil) {
NSError *fileReadError;
NSFileWrapper *videoFileWrapper = [[NSFileWrapper alloc] initWithURL:self.videoURL options:NSFileWrapperReadingWithoutMapping error:&fileReadError];
if(fileReadError){
NSLog(#"File read error: %#", [fileReadError localizedDescription]);
}else {
[videoFileWrapper setPreferredFilename:#"video.mov"];
[self.fileWrapper addFileWrapper:videoFileWrapper];
}
}
//...
}
It's not totally clear from the documentation, but I'm 99% positive that NSFileWrapper will memory-map everything. The main use case for NSFileWrapper is to embed files inside documents, where you'd presumably need access to said file at all times.
What are you trying to use the file wrapper for? I assume if you're mapping an entire directory it's not necessarily to embed media inside a document, but perhaps I'm mistaken. If you maybe talk a bit more about your use case, as that will influence what alternatives you might go for.

Resources