IOS Memory Management - writing video to temp file - ios

I'm creating an app that plays back multiple videos and photos in a snapchat story fashion. I fetch each of these videos data and write it to a temp file in order to play with AVPlayer.
NSString *outputPath = [[NSString alloc] initWithFormat:#"%#%#", NSTemporaryDirectory(), [NSString stringWithFormat:#"output%i.mov", i]];
[data writeToFile:outputPath atomically:YES];
In viewWillDissapear I delete all of these files with:
NSArray* tmpDirectory = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:NSTemporaryDirectory() error:NULL];
for (NSString *file in tmpDirectory) {
[[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:#"%#%#", NSTemporaryDirectory(), file] error:NULL];
}
But when I look at how much memory my app is using, in settings, its around 300 mb. Is there a way to see what is actually taking up all this memory? I would think this is high since I'm not caching anything besides the currentUser data (name, profileImage).
Should I be doing something else to clean the temp files?

But this line is absolutely wrong:
[data writeToFile:outputPath atomically:YES];
The implication is that at some moment — this moment — you are holding an entire video in memory (as data). That is completely incorrect; it should never happen. You should download to a file on disk, and if necessary, write that file to another file; you should never attempt to load the entire video into memory at once.

As #peter mentioned, you can use streaming Video streaming.
Or else If you really want to download video file and store it into the disk space and want to utilize it later part then you should follow apple recommended way i.e NSFileHandle class to optimize your application heap memory while downloading larger files.
NSFileHandle

Related

AVAudioRecorder : Can we record without saving into file?

I am wondering is there any way we can record audio in an iOS app so that it don't get saved into any file because my file size is getting into GBs.
Thanks!!
If You are recording audio that means you have to save it somewhere, because it must need some space otherwise how you can record ?
You can use NSTemporaryDirectory to save your file like,
NSString *folderPath = NSTemporaryDirectory();
NSString *soundFilePath = [folderPath
stringByAppendingPathComponent:[NSString stringWithFormat: #"%.0f%#", [NSDate timeIntervalSinceReferenceDate] *1000.0, #"audio.caf"]];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
NSLog(#"Using File called: %#",soundFileURL);
audioRecorder = [[ AVAudioRecorder alloc] initWithURL:soundFileURL settings:recordSetting error:&error];
[audioRecorder setDelegate:self];
[audioRecorder prepareToRecord];
Above is the code snippet so that you get idea!
ios automatically clears NSTemporaryDirectory when needed so it is not point of worry for you. And yes you have to store recorded audio somewhere if you want it for permanent!
You can create your own directory that can be work as temp directory that you can clear manually where needed! (create directory in document directory record file there and clear it when your task will be completed)
But you can't record without storing it!
Imagine you could -- would it be better? Keeping GBs of data in RAM?.. Sounds bad.
If you want to keep your disk space free, what you could do is:
1) Save small part of data into file (i.e. 20 MB?)
2) Send this file to backend and delete it from disk on completion
3) Repeat process until you finish recording

Local saved videos don't play again after app closes

This is strange, but basically I download and save a video locally, and the store the url path to provide to an AVPlayer to play.
This works fine the first time I do it. I download a file, and then I can play it to my hearts content as many times UNTIL I exit the app. When I launch the app a second time, I now get a black screen when I try to play the same exact video using the same exact path.
Because I am using the Simulator I can verify that the videos and pictures indeed very much still exist in the same folder I saved them to, and I can still play them if I click on them from the Finder.
Maybe it's a caching issue? If it matters, I've saved them straight to the Library directory as I test this.
Relevant Code:
NSString *outputFile = [NSString stringWithFormat:#"video_%#.mp4", guid];
NSString *outputDirectory = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *tempPath = [outputDirectory stringByAppendingPathComponent:outputFile];
NSURL *fileURL = [NSURL fileURLWithPath:tempPath];
// save the video to the URL
Then I "persist" it using an NSString [fileURL path] (The way I've built this out, assume the solution requires an NSString to NSURL conversion).
Later I create an AVPlayerItem:
NSURL *url = [NSURL fileURLWithPath:persistedObject.contentURL];
NSLog(#"url: %#", url); // prints a valid location**
AVPlayerItem *item = [AVPlayerItem playerItemWithURL:url];
** for example this is a sample url location
url: file:///Users/gabriel/Library/Developer/CoreSimulator/Devices/CE1FC933-808C-4003-9BE4-DEC59B787FF7/data/Containers/Data/Application/FAD072B4-B5B0-4487-8A76-57B047324A00/Library/picture_D8DEAFA5-0843-4AA3-BB32-C61E32D13579.mp4
It's been suggested I use URLForDirectory:inDomain:appropriateForURL:create:error: and URLByAppendingPathComponent: instead, which I will look into. But still confused as to why it would play when I first download it, but not after app exits when it's the same exact file.
You've made a classic mistake. You are persisting the full path. But the full path changes. Never persist a full path. Only persist the part of the path relative to the value obtained from NSSearchPathForDirectoriesInDomains.
Given what you are doing, you should only persist the base filename (outputFile). Then when the app starts, you rebuild the full path again like you did originally but use the persisted filename to append it to the dynamically obtained path to the application support folder.

Save a NSArray to iCloud and retrieve it

I have an app that currently saves a NSMutableArray to a file in the documents directory on the iPad. This is saved by
[residentData writeToFile:filePath atomically:NO];
and is read back in by;
residentData = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
I need to write this file to iCloud, as the data needs to be available across several devices. I have tried the following to create the file in iCloud;
NSData *myFileData = [NSKeyedArchiver archivedDataWithRootObject:residentData];
[fileManager createFileAtPath:[icloudURL path] contents:myFileData attributes:nil];
Which does indeed create a file in the correct place. I am struggling though to understand how to read this file back into my NSMutableArray (residentData). I have tried;
residentData = [NSKeyedUnarchiver unarchiveObjectWithData:myFileData];
But this doesn't decode into the array correctly. Please can someone show me the error of my ways - I suspect a fundamental misunderstanding on my part of NSData is to blame, but any pointers would be welcome.
It finally clicked, my approach was wrong. I don't need to use any of the above. Once I have done the;
[fileManager setUbiquitous:YES itemAtURL:currentPlace destinationURL:icloudURL error:&error];
The system takes care of everything else - i.e. just accessing the file normally with
residentData = [[NSMutableArray alloc] initWithContentsOfURL:icloudURL];
Has worked.

Terminated due to memory error iOS?

I am working on a project where I need to manage videos. I need to rename or delete video. For that we need to hold the video in NSDATA and then manage it.
But I am getting an error message as Terminated due to memory error on below statement.
Edited
NSData *data=[NSData dataWithContentsOfFile:self.path];
if (data){
BOOL success = [data writeToFile:videopath atomically:NO];
}
self.path contains the path of video file. It works in small size video (of 4-10 mins) But it crashes in large size video (bigger than 20-30 mins).
Please advice.
Use this code instead loading the video file to memory, Your code will work with small files but u gonna fail with big files.
if ( [[NSFileManager defaultManager] isReadableFileAtPath:source] ){
[[NSFileManager defaultManager] copyItemAtURL:source toURL:destination error:nil];}
You are maintaing the full video as NSData inside the application. Instead of using a Video file as NSData, copy the video to some where (e.g NSTempoaryDirectoy). You can delete or rename the Old video.
I am not sure what the requirement for you. from your question I understood that you need to rename your video file. for renaming, why we need to go for reading it as NSdata and again writing the same. for Renaming try the below code.
NSFileManager *filemanager = [NSFileManager defaultManager];
if ([filemanager fileExistsAtPath:filePath])
{
NSString *target = [[filePath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newnameofthefile];
[filemanager moveItemAtPath:filePath toPath:target error:nil];
}
I hope this may help you..

How can I save document files to the SQLite Database in iOS to view them later in the program?

I'm having a problem with the code I'm writing.
I'm writing an iOS program (I'm an iOS rookie) which basically requires me to use quick look framework to view some documents on the iPhone (*.doc, *.ppt, *.pdf etc..) which are stored in the database (Core Data - SQLite, nothing external). I need to add the files somehow, but since iOS isn't really allowing me to browse through its file system I can't find and save the documents i need in database. Which kinda blocks everything else i need to do until I can get those documents from the database. (to set up table views that list the files and the details about the files etc.)
This is for a class project so it doesn't need to be perfect condition, I just need to be able to browse through a few documents while I'm presenting the project. I can add all the documents I'm going to use at one time while I'm coding and I won't need to be able to add any new files when I'm using the program during the presentation. And I don't want it to make it more complicated if i don't have to. Like connecting to an external database with the files already saved in and use a php buffer-page to connect to that database or anything like that. I don't have the necessary server system to execute that php file. I want this operation to be done inside the phone.
The solutions I was able to think of so far:
Grab some random office files from the internet and save them into the database. Then use them later.
Create image scans of some office files and "cheat" by using the scanned image instead of actual documents.
I would really appreciate it if someone can tell me another and much easier way to handle this. Please just keep in mind that while I have a programming background with Java and C#, I'm still an iOS rookie and just barely moving on from scratching the surface. So it is likely that I don't know about something iOS provides by default and I'm just pulling my hair out for nothing.
I think thats it, I hope I didn't forget anything. If you need more details I'm going to be here and I can provide them almost instantly. Thanks everyone in advance for your help.
It sounds like NSFileManager will help you.
If you place your documents into your project tree, they will be available to your app. Use NSFileManager to copy them into the app's Documents folder using something like:
- (void)placeBundleFileInDocuments:(NSString *)filename
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:[[filename lastPathComponent] stringByDeletingPathExtension] ofType:[filename pathExtension]];
NSString *documentsFolderPath = [NSString stringWithFormat:#"%#/", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]];
NSString *path = [documentsFolderPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#", filename]];
if ([fileManager fileExistsAtPath:path])
return;
NSError *error = nil;
[fileManager copyItemAtPath:bundlePath toPath:path error:&error];
if (error) {
NSLog(#"Unable to copy file (%#).", error.localizedDescription);
}
}
Then, you can use NSFileManager to retrieve details about the files. You might find this method useful:
- (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error
I hope this helps!

Resources