AVPlayer playerWithURL not working after app restart - objective-c

I am picking a video from the user photo library and than I save the video in the user Documents Folder to be able to play the video even if the user deletes this video from his photo Library. The URL to this file is stored in Core Data.
Everything works fine until the next time I run the App. Somehow it seems like the URL is no longer valid, which is strange because I am able to delete the video file when [AVPlayer playerWithURL:videoURL] fails.
Here is how I pick the video URL:
- (void) imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
NSURL* videoURL = info[UIImagePickerControllerMediaURL];}
This is how I save the video:
+ (NSURL*) saveVideoInDocumentsFolder:(NSURL*)videoURL name:(NSString*)name {
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* pathComponent = [NSString stringWithFormat:#"/%#.%#", name, [videoURL pathExtension]];
NSString* path = [documentsDirectory stringByAppendingPathComponent:pathComponent];
NSError* error = nil;
NSData* videoData = [NSData dataWithContentsOfURL:videoURL options:0 error:&error];
if (error)
return nil;
BOOL success = [videoData writeToFile:path options:NSDataWritingAtomic error:&error];
if (success)
return [NSURL fileURLWithPath:path];
return nil;}
This is how I play the video:
AVPlayer* player = [AVPlayer playerWithURL:videoURL]; // <- AFTER I RESTART THE APP THIS METHOD ALWAYS RETURNS nil!!
AVPlayerViewController* viewController = [[AVPlayerViewController alloc] init];
[self presentViewController:viewController animated:YES completion:nil];
viewController.player = player;
[player play];
Many thanks in advance!

The URL to this file is stored in Core Data
That's the problem. The documents directory URL changes every time you run the app (because you are sandboxed), so it isn't valid the second time. Never never never save an absolute file URL in iOS!

You need to take every time the path directory when close and open app , because when app close it it removes documentDirectory, so you have to take it another time , and if you want to get your video file then save the FILE NAME of that video into preference or Core Data then get documentDirectory path by appending this video name file and you will get your video.
Just have a look: - (code is in swift you can convert it objective c easily)
let fileName = "recording.mp4"
let tempPath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
let tempDocumentsDirectory: AnyObject = tempPath[0] as AnyObject
let tempDataPath = tempDocumentsDirectory.appendingPathComponent(fileName) as String as String
that FILE NAME you will save in preference or coreData
and when you close and open the app just check do you have FILE NAME save in preference or in core data if yes then take it by appending with documentsDirectory and you will get your video

Related

Objective c - picker to get video path

In my app, I allow the user to record videos and I save them in this path:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [paths[0] stringByAppendingPathComponent:DestFileName];
I want to implement a video picker in my app, but the objective is to get the path of the video. I started with the basic method like this:
UIImagePickerController *videoPicker = [[UIImagePickerController alloc] init];
videoPicker.delegate = self;
videoPicker.modalPresentationStyle = UIModalPresentationCurrentContext;
videoPicker.mediaTypes =[UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypePhotoLibrary];‌​
videoPicker.mediaTypes = #[(NSString*)kUTTypeMovie, (NSString*)kUTTypeAVIMovie, (NSString*)kUTTypeVideo, (NSString*)kUTTypeMPEG4];
videoPicker.videoQuality = UIImagePickerControllerQualityTypeHigh;
[self presentViewController:videoPicker animated:YES completion:nil];
The first problem is that I can not access to my recorded videos, why?
Then, this picker allows the user to play video, and when I choose one video I think that a compressed video is created and sent to our delegate method (the URL goes to a tmp repository).
I don't want to allow the user to play video and I just want the video path, is it possible with this method?
I can also get the list of files in
NSSearchPathForDirectoriesInDomains
with this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *rootPath = path[0];
[[NSFileManager defaultManager] createDirectoryAtPath:rooPath withIntermediateDirectories:YES attributes:nil error:nil];
NSArray* mPaths = [[NSFileManager defaultManager] contentOfDirectoryAtPath:rootPath error:nil];
With this method I can found my recorded videos, but then I have to implement my own custom picker, but it can be difficult and long to implement (need to get the thumbnail for example, possible?).
To summarize:
With the UIImagePickerController, is it possible to prevent the play, to just get the path and to show our recorded videos?
With the NSFileManager, is it possible to easily create a custom video picker with thumbnail, duration etc.)
Thanks for your help
UIImagePickerController is an instance of the iOS Photos App by which you can access all of the Photos/Videos in the Device Gallery and not exclusively those belonging to your App. There is no way to have the UIImagePickerController show only the videos recorded by your App. It is also not possible to have the UIImagePickerController skip the Play/Choose step while picking a File.
Like you mentioned you can definitely write the Video file into the Apps documents directory by means of NSFileManager and hold a reference to the path which you can use for later use. I think you would probably end up creating a Model Class say "Video" with the following attributes;
a) savedPath
b) duration
c) thumbnail etc
You can then use something like a NSKeyedArchiver to encode these objects and store into a File so that you can retrieve this information later.
I finally found the solution using a custom picker, :
To get the list of local videos:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* rootPath = paths[0];
[[NSFileManager defaultManager] createDirectoryAtPath:rootPath withIntermediateDirectories:YES attributes:nil error:nil];
NSArray* mPaths = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:rootPath error:nil];
To get the duration and the thumbnail:
NSURL *videoURl = [NSURL fileURLWithPath:mPath];
AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURl options:nil];
AVAssetImageGenerator *generate = [[AVAssetImageGenerator alloc] initWithAsset:asset];
generate.appliesPreferredTrackTransform = YES;
NSError *err = NULL;
CMTime time = CMTimeMake(1, 60);
CGImageRef imgRef = [generate copyCGImageAtTime:time actualTime:NULL error:&err];
UIImage *img = [[UIImage alloc] initWithCGImage:imgRef];
[m_videoFrames addObject:img];
CMTime duration = [asset duration];
int seconds = ceil(duration.value/duration.timescale);
Thanks!

Saved image does not appear in file directory

I'm using the following code below save an image locally. It works without any errors, and I can preview the incoming image. The only problem is that the image never seems to actually be saved or appear in the Images directory. I use iExplorer to double check, and I have refreshed the folder and the image is still not there. Your thoughts are appreciated.
// I can preview this UIImage and it appears as expected
UIImage *image = [UIImage imageWithData:responseObject]; //responseImage is an image from online
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *docs = [paths objectAtIndex:0];
NSString* path = [docs stringByAppendingFormat:#"/Images/image1.jpg"];
NSData* imageData = [NSData dataWithData:UIImageJPEGRepresentation(image, .8)];
NSError *writeError = nil;
if(![imageData writeToFile:path options:NSDataWritingAtomic error:&writeError]) {
//This never fires, so you would think the image would have saved, but that does not appear to be the case!
NSLog(#"%#: Error saving image: %#", [self class], [writeError localizedDescription]);
}
I did also check to see if the file exists programmatically and apparently it does exist. However, when I try to reference it within a UIWebview, it doesn't load anything for that image.
Remember that NSFileManagers use NSString paths to read/write files, however UIWebViews use NSURLs. In order for you to load the file into a UIWebView, you'll need to convert your NSString path into an NSURL file URL.
So instead of something that looks like:
/Documents/Path/To/File.png
It needs to be
file:///Documents/Path/To/File.png
I believe the correct way to do this is to use the [NSURL fileURLWithPath:] method.
So it would look something like this:
NSFileManager* fileManager = [NSFileManager defaultManager];
NSString* path; //However you got your path here
if([fileManager fileExistsAtPath:path])
{
NSURL* fileURL = [NSURL fileURLWithPath:path];
NSURLRequest* request = [NSURLRequest requestWithURL: fileURL];
[_myWebView loadRequest:request];
}
This is too long to post as a comment, so I'm posting it as an answer.
I'm not sure about your if statement there. It does return a BOOL NO if the file operation fails, but I'm not exactly sure what "the operation fails" means. Just because the file is not written out does not necessarily mean that the operation failed. So it's better to check the NSError itself.
Instead of checking ![writeToFile], run the line without the if statement, and then check if(writeError != nil). If the if statement is true, then something went wrong, and if so, you can check the localized description of the error.
So to recap,
NSError* writeError = nil;
[imageData writeToFile:path options:NSDataWritingAtomic error:&writeError];
if(writeError != nil)
{
//Something went wrong
NSLog("File write error: %#", writeError.localizedDescription);
}

Vfr-Reader crashes on assert(range.location != NSNotFound);

I am using thisopen source control called Vfr Reader to show pdf files in my app. All was working on iOS 7. After iOS update app crashes when I try on open a pdf file.
I am using this code to initialize pdf reader.
NSString *fileName = [[NSBundle mainBundle] pathForResource:#"PdfName" ofType:#"pdf"];
ReaderDocument *document = [ReaderDocument withDocumentFilePath:fileName password:nil];
if (document != nil)
{
ReaderViewController *readerViewController = [[ReaderViewController alloc] initWithReaderDocument:document];
readerViewController.delegate = self;
readerViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
readerViewController.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:readerViewController animated:YES completion:Nil];
}
App crashes in this fuction of vfr
+ (NSString *)relativeFilePath:(NSString *)fullFilePath
{
assert(fullFilePath != nil); // Ensure that the full file path is not nil
NSString *applicationPath = [ReaderDocument applicationPath]; // Get the application path
NSRange range = [fullFilePath rangeOfString:applicationPath]; // Look for the application path
assert(range.location != NSNotFound); // **Crashes here**
return [fullFilePath stringByReplacingCharactersInRange:range withString:#""]; // Strip it out
}
On crash debugger shows these values.
fullFilePath = #"/Users/GuruTraxiOSDev01/Library/Developer/CoreSimulator/Devices/CC9412A6-9A95-4F46-89BA-8ECC13D0AF19/data/Containers/Bundle/Application/D2DC440B-F010-4575-93FD-3CB05BFF4F78/AppName.app/PdfName.pdf" 0x798c9b30
range = location=2147483647, length=0
applicationPath =#"/Users/GuruTraxiOSDev01/Library/Developer/CoreSimulator/Devices/CC9412A6-9A95-4F46-89BA-8ECC13D0AF19/data/Containers/Data/Application/32D612DE-FFD2-4C1E-B403-CDA177B460A6" 0x798b46b0
I already confirmed the file's existence.
Can anyone help please!
EDIT: This question solved crash on file load. But app still crashes on
CGContextDrawPDFPage(context, thePDFPageRef);
I was facing the same issue, so I made some changes to the Library Files which should not be an option as such but in this case I didn't have any choice to get it to work. So to make your code work follow the instruction below:
Go to ReaderDocument.m file and make the following changes:
+ (NSString *)documentsPath
{
//Make changes to return the NSBundle path.
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSFileManager *fileManager = [NSFileManager new]; // File manager instance
NSURL *pathURL = [fileManager URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
// return [pathURL path]; // Path to the application's "~/Documents" directory // Code changes.
return bundlePath;
}
If you had a breakpoint just delete it, that solve it for me.
Breakpoint navigator => select the breakpoint then delete

Objective C can't read text file when running on iPhone

I've made an app that is relying on reading and writing a plist-file. This works well when I'm running the app in the iPhone simulator, but doesn't work at all when I'm testing it on my iPhone. I've also made a pre made text file in .txt format with demo data. The app works when I'm running this file.
All the reading and writing is done in a class that looks like this:
-(void)saveArray:(NSMutableArray *)inputArray
{
albumArray = inputArray;
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [path objectAtIndex:0];
NSString *filePath = [documentFolder stringByAppendingFormat:#"albums.plist"];
[albumArray writeToFile:filePath atomically:YES];
}
Update: Changed the string from "stringByAppendingFormat" to "stringByAppendingPathComponent" and it seems to work now. Thanks a lot! You guys made my day made.
Are you sure, that the folders already exist?
Here is a function i'm using to get the path to my file:
- (NSString*) pathToSavedAlbums
{
NSURL *applicationSupportURL = [self applicationDataDirectory];
if (! [[NSFileManager defaultManager] fileExistsAtPath:[applicationSupportURL path]])
{
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:[applicationSupportURL path]
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error)
{
NSLog(#"error creating app support dir: %#", error);
}
}
NSString *path = [[applicationSupportURL path] stringByAppendingPathComponent:#"albums.plist"];
return path;
}
Check the spelling of the plist name as well as the case, device is case sensitive for docs but simulator isn't. Also try deleting the app from the device and reinstalling it ?
writeToFile:atomically: returns a bool, so check that to see if it fails to even write to the path. Check the file path string and ensure this is where you want it to go.

Saving a string into file in Objective-C (iPhone)

I seem to have stumbled over a problem regarding saving an xml file from a string (this is done on the iPhone)
The file itself exists and included in the project (hence within the workspace), and all indications I get from the code snippet which follows passes without any errors on the emulator and fail on the iPhone (error 513), but in either case the file is not saved!
{
Hits = config->Hits;
NSString* filenameStr = [m_FileName stringByAppendingFormat: #".xml" ];
NSString* pData = [self getDataString]; // write xml format - checked out ok
NSError *error;
/* option 2 - does not work as well
NSBundle *mainBundle = [NSBundle mainBundle];
NSURL *xmlURL = [NSURL fileURLWithPath:[mainBundle pathForResource: m_FileName ofType: #"xml"]];
if(![pData writeToURL: xmlURL atomically: true encoding:NSUTF8StringEncoding error:&error])
{
NSLog(#"Houston - we have a problem %s#\n",[error localizedFailureReason]);
return false;
}
*/
if(![pData writeToFile: filenameStr atomically: FALSE encoding:NSUTF8StringEncoding error:&error])
{
NSLog(#"Houston - we have a problem %s#\n",[error localizedFailureReason]);
return false;
}
return true;
}
Any help would be appreciated,
-A
You should not write to files included in the application package. On a real iPhone, you may be prevented from doing this because these files are digitally signed.
Even if you can modify a packaged file, it is not a good place to store data. Re-installing the application from an App Store upgrade or an Xcode build will overwrite the file with the original.
Instead, store your XML into the Documents directory. You can get the path like this:
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* leafname = [m_FileName stringByAppendingFormat: #".xml" ];
NSString* filenameStr = [documentsDirectory
stringByAppendingPathComponent:leafname];
If your file needs some initial state that you don't want to generate in your code, have your app check that it is present in the documents directory the first time it is needed and, if it is missing, copy it from the template in the package.
An alternative to storing structured data is to use user defaults. For example:
[[NSUserDefaults standardUserDefaults] setObject:foo forKey:FOO_KEY];