iOS Mediaplayer and URL redirection - objective-c

I'm trying to implement a MPMedia player to play internet files. So far I have managed to open the media player with links to videos hosted by me. The problem starts when I try to load an url that doesn't directly map to a file but instead gets redirected to the file.
The initial url looks like this: h*tp://www.example.com/numbers/numbers/numbers
and when I put it in my browser it is automatically changed for something like this: h*tp://www.example.com/numbers.mp4?to=numbers
When I use the initial url directly, just like I was doing with direct urls, the player comes up and inmediatly goes down without playing the video.
I have tried NSURLConnection to get the redirect url but I havenĀ“t been able to get it working, somehow the method - connection:willSendRequest:redirectResponse: doesn't get called. I think it may be because it has been removed in iOS 5, but I don't know any alternatives, and this kind of problems are really undocumented.
Here is my code: (I've skipped the shortUrl initialization, it works).
NSLog(#"%#",shortUrl);
redirecionando=TRUE;
NSURLConnection * conection = [[NSURLConnection alloc]initWithRequest:[NSURLRequest requestWithURL:shortUrl] delegate:self];
[conection start];
while(self.redirecting);
self.moviePlayerController = [[MPMoviePlayerViewController alloc] initWithContentURL:finalurl];
moviePlayerController.view.frame = self.view.bounds;
[self presentMoviePlayerViewControllerAnimated:moviePlayerController];
moviePlayerController.moviePlayer.controlStyle = MPMovieControlStyleFullscreen;
moviePlayerController.moviePlayer.shouldAutoplay = YES;
[moviePlayerController.moviePlayer prepareToPlay];
moviePlayerController.moviePlayer.fullscreen=YES;
}
- (NSURLRequest *)connection: (NSURLConnection *)inConnection
willSendRequest: (NSURLRequest *)inRequest
redirectResponse: (NSURLResponse *)inRedirectResponse;
{
finalurl=inRequest.URL;
self.redirecting=FALSE;
return inRequest;
}
I know that this last methos isn't getting called, I NSLogged it. Right now, when the above code gets executed it just waits because of the while loop.
By the way, I cannot use an external api to discover the redirection like http://longurl.org/ because the link returns a token access for the device that asks for
it.
Thanks in advance.

I solved it. It turs out that with the line:
while(self.redirecting);
I was bloking the main thread and thus preventing the method connection: willSendRequest: redirectResponse: from being called. I've solved it getting the code below that line and putting it into another function called from the redirect method. I've also had to modify the redirect method because it got called many times, even with the original url before arriving at the mp4.
Thanks if you took the time to read the question.

Related

How to get message-body immediately after receive a HTTP header by using NSURLSession?

I want to use NSURLSession to receive a xml stream from server and display each xml immediately on the screen.
Here is my delegate code:
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
// Enumerate each message-body.
[data enumerateByteRangesUsingBlock:^(const void * _Nonnull bytes, NSRange byteRange, BOOL * _Nonnull stop) {
// Convert message-body to xml string.
NSString *string = [[NSString alloc] initWithBytes:bytes
length:byteRange.length
encoding:NSUTF8StringEncoding];
dispatch_async(dispatch_get_main_queue(), ^{
// Some code to display string.
// ...
});
}];
}
This code works fine except one problem.
The problem is, sometimes when receiving stream, the didReceiveData doesn't be called immediately after didReceiveResponse, it sometimes receive more than one HTTP messages, and then call didReceiveData once to pass all messages which it just receive for me.
It can sometimes take a while for receiving multiple messages, and makes my application not able to display the xml in realtime.
Is there any configuration or property can make it call didReceiveData immediately? I read the document but find nothing useful.
Thanks a bunch.
Update:
I tried to use NSURLConnection to do the same things, it runs perfectly without this problem.
Each didReceiveData is called behind didReceiveResponse immediately.
How can I make the didReceiveData of NSURLSession work just like NSURLConnection?
IIRC, NSURLSession should send data as it receives it, but only after it receives a certain about of data, or after a period of time.
If you're trying to get individual chunks of data, you might instead consider sending them back from the server as a multipart response. Each "part" would contain one of your messages, and you would get a new didReceiveResponse: callback between each one.
With that said, I'm not sure why NSURLConnection would behave differently. They use a lot of the same code under the hood. You might try filing a bug with Apple.

How to ensure UIManagedDocument is ready before NSFetchedResultsController method is called?

I am totally stuck on this one. My basic problem is that my:
- (NSFetchedResultsController *)fetchedResultsController
Method is crashing when it tries to read my Core Core entity, as the managedObjectContext / UIManagedDocument is nil. At the moment I think it is because my UIManagedDocument is not open / ready. So for the last 3 hours I have been trying to make it so my delegate method is not fired until the document is open.
This is the code that I am using to get the document:
if (!self.document) {
[[CATManagedDocumentHandler sharedDocumentHandler] performWithDocument:^(UIManagedDocument *document) {
self.document = document;
}];
}
This works fine at any other place in my app, but it seems as the opening process is just not quick enough for the delegate methods in my tableView.
Links I have looked at so far:
http://omegadelta.net/2011/05/10/how-to-wait-for-ios-methods-with-completion-blocks-to-finish/
Executing Code Block In Place Of #selector
About calling of dispatch_queue_t and dispatch_sync
Grand Central Dispatch (GCD) vs. performSelector - need a better explanation
GCD to perform task in main thread
iOS - how to be notified when a thread (using GCD) ends it's job
I have tried: Blocking main thread until I get NSNotification (set up in CATManagedDocumentHandler) & Blocking main thread until I get a block call back.
Neither of these work. My app just freezes. Am I think about this wrongly? How can I get the delegate method to wait until my document is open / ready? Or is there a different approach I should be taking with this?
Thanks
Carl.
ok, when your app first starts, I would suggest checking whether 1. your database exists (if it doesn't, you alloc init it) and 2. if the document doesn't exist (on disk), is closed or is open.
here's how you could do this:
looks like you are using a singleton for your database, so when your first view controller comes up, check whether the Managed Document has been alloc inited, so in ViewWillAppear:
if (!dataBase)
{
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"Your DB Name"];
self.dataBase=[[UIManagedDocument alloc]initWithFileUrl:url];
}
so now if the database (UIManagedDocument) has been alloc inited, it still does not mean that the actual database file has been created on disk. You have to check for it as follows (and you could do this in a setter for your database or in ViewDidLoad, but don't do it in another thread because it won't work)
so you are checking for 3 cases here: does not exist
if (![[NSFileManager defaultManager]fileExistsAtPath:[yourManagedDocumentFromSingleton.fileUrl path])
{
[[NSFileManager defaultManager]fileExistsAtPath:[yourManagedDocumentFromSingleton saveToUrl:[[NSFileManager defaultManager]fileExistsAtPath:[yourManagedDocumentFromSingleton.fileUrl forSaveOperation:UIDocumentSaveForCreating completionHandler:
^(BOOL success){ now you can use your uidocument and its managed object context}];
}
if file does exist but is closed:
else if (yourManagedDocumentFromSingleton.documentState==UIDocumentStateClosed)
{
[yourManagedDocumentFromSingleton openWithCompletionHandler:
^(BOOL success) {use your document here}];
}
finally if the document is already open, just use it:
else if (yourManagedDocumentFromSingleton.documentState=UIDocumentStateNormal)
{
//do whatever with the document
}
one important thing to mention is that UIDocument is not thread safe. So it must be used and checked in the same thread that it was created (presumably main thread here). Otherwise it will not work.
I don't know the exact structure of your view controller or singleton but if you follow these steps it will work.
PS. Also make sure that once your doc is up and running and you're adding items to it or removing, save it after each operation so your NSFetchedResultsController gets updated. CoreData does have autosave but I found that I had to manually save for things to work properly. You can save with (from previous method):
forSaveOperation:UIDocumentSaveForOverwriting

UIWebView - shouldStartLoadWithRequest - NSMutableURLRequest

I have an UIWebView embeded in my app. What I am doing is, add an header (to be specific, authorization header), to all requests made from it.
This event is fired when a URL opens from an HTML iframe BUT it does not seem to be able to add header to that request.
To be sure, I added a log entry in this event and it does well but if I monitor HTTP traffic using burpsuite, it's not adding header.
Has anybody encountered such issue ever? Any possible workaround?
Below is my code, if it helps.
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
[(NSMutableURLRequest *)request addBasicAuth:self.accountObj.Username andPassword:self.accountObj.Password];
NSLog(#"!Request URL :%#",[request URL]);
NSLog(#"!Request Header :%#",[request allHTTPHeaderFields]);
return YES;
}
Instead of adding a header to your actual request, why dont you create a new one?
Cancel that one, and throw a new one

Set Image on Application Startup

I'm currently developing an Obj-C Desktop application which gets a string from an URL and displays an image regarding to the recieved string.
//I know this one won't work, but it's just for example
NSString * text = [NSString stringFromUrl:#"http://example.com"];
NSString * imageName = [text splitStringIntoSome];
//imageName is now #"A.png"
[imageViewOutlet setImage:[NSImage imageNamed:imageName];
So, the problem is, I want to do that when the application launches, but when I copy this code into my applicationDidFinishLaunching, just nothing happens...
Unfortunately since URL requests are performed asynchronously, it requires a bit more code to do this. Here is a good example straight from Apple:
Using NSURLConnection
Except in - (void)connectionDidFinishLoading:(NSURLConnection *)connection you would create an image using NSImage's - (id)initWithData:(NSData *)data

Prevent warning when NSDocument file is programmatically renamed

My application allows the user to rename documents that are currently open. This is trivial, and works fine, with one really annoying bug I can't figure out. When a file is renamed, AppKit (kindly) warns the user the next time they try to save the document. The user says "OK" and everything continues as normal. This makes sense when something external to the application changed the document, but not when it was actually done by the document itself.
The code goes something like this:
-(void)renameDocumentTo:(NSString *)newName {
NSURL *newURL = [[[self fileURL] URLByDeletingLastPathComponent]
URLByAppendingPathComponent:newName];
NSFileManager *fileManager = [NSFileManager defaultManager];
[fileManager moveItemAtURL:[self fileURL] toURL:newURL];
NSDictionary *attrs = [fileManager attributesForItemAtPath:[newURL path] error:NULL];
[self setFileURL:newURL];
[self setFileModificationDate:[attrs fileModificationDate]];
}
One would think that expressly setting the new URL and modification date on the document would be enough, but sadly it's not. Cocoa still generates the warning.
I've tried changing the order (setting the new URL on the document, THEN renaming the file) but this doesn't help.
I've also tried a fix suggested by a user on an old post over at CocoaDev:
[self performSelector:#selector(_resetMoveAndRenameSensing)];
Even this does not stop the warning however, and I'm guessing there has to be a proper way to do this using the documented API. How does Xcode handle things when a user clicks a file on the project tree and renames it to something else. It doesn't warn the user about the rename, since the user actually performed the rename.
What do I need to do?
There isn't much on this in the main docs. Instead, have a look at the 10.5 release notes: http://developer.apple.com/library/mac/#releasenotes/Cocoa/AppKitOlderNotes.html%23X10_5Notes under the heading "NSDocument Checking for Modified Files At Saving Time"
(In the case of Xcode, it has a long history and I wouldn't be surprised if if doesn't use NSDocument for files within the project)
It is worth noting that moving a file does not change its modification date, so calling -setFileModificationDate: is unlikely to have any effect.
So one possibility could be to bypass NSDocument's usual warning like so:
- (void)saveDocument:(id)sender;
{
if (wasRenamed)
{
[self saveToURL:[self fileURL] ofType:[self fileType] forSaveOperation:NSSaveOperation delegate:nil didSaveSelector:nil contextInfo:NULL];
wasRenamed = NO;
}
else
{
[super saveDocument:sender];
}
}
Ideally you also need to check for the possibility of:
Ask app to rename the doc
Renamed file is then modified/moved by another app
User goes to save the doc
At that point you want the usual warning sheet to come up. Could probably be accomplished by something like:
- (void)renameDocumentTo:(NSString *)newName
{
// Do the rename
[self setFileURL:newURL];
wasRenamed = YES; // MUST happen after -setFileURL:
}
- (void)setFileURL:(NSURL *)absoluteURL;
{
if (![absoluteURL isEqual:[self fileURL]]) wasRenamed = NO;
[super setFileURL:absoluteURL];
}
- (void)setFileModificationDate:(NSDate *)modificationDate;
{
if (![modificationDate isEqualToDate:[self fileModificationDate]]) wasRenamed = NO;
[super setFileModificationDate:modificationDate];
}
Otherwise, your only other choice I can see is to call one of the standard save/write methods with some custom parameters that prompt your document subclass to move the current doc rather than actually save it. Would be trickier I think. Perhaps define your own NSSaveOperationType?
With this technique the doc system should understand that the rename was part of a save-like operation, but it would need quite a bit of experimentation to be sure.
Much inspired from #Mike's answer, I got the "moved to" message not to show up anymore by re-routing NSSaveOperation to NSSaveAsOperation. In my NSDocument subclass:
I overload saveDocumentWithDelegate:didSaveSelector:contextInfo: to determine the save URL and document type (assigning those to self); if the old fileURL exists, I move that to the new location
Inside saveDocumentWithDelegate:didSaveSelector:contextInfo: I redirect the call to [self saveToURL:self.fileURL ofType:self.fileType forSaveOperation:NSSaveAsOperation completionHandler: ...] instead of [super saveDocumentWithDelegate:didSaveSelector:contextInfo:]
This works for me.
Isn't it possible to programmatically answer the question for the user?
Or you can save immediately after renaming, this way a user gets every answer in one go.
I see that this problem is up and running for some time, so telling you to read the reference won't do any good i guess..
Hope i helped a little bit although it doesn't fix your problem directly