I use NSURLConnection delegate methods to download either a video or audio file, the download is OK, now i need to play it right after it has finished downloading.
My relevant code is this:
-(IBAction)downloadAndPlay:(id)sender{
NSString *fileUrlPath=[host stringByAppendingString:rowContent];
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:fileUrlPath]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
receivedData = [NSMutableData data] ;
//NSLog(#"connection succeeded");
} else {
// Inform the user that the connection failed.
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"Music Album" message:#"Connection failed, please try again" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}
}
//NSURLConnection delegate methods
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
// receivedData is declared as a method instance elsewhere
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
}
How can i play the file (either audio or video) right after it is downloaded (auto play).
Thanx in advance.
You will need to save the data you've downloaded to a file, and then open the file using a MPMoviePlayerViewController or similar. Right now you just download your file as NSData, and leave it at that.
So save your NSData object to the file system with the appropriate name/extension (or just write your download to the file system itself, which makes more sense for larger files), get a file URL handle, and pass it to your media player.
Related
I never use web API and don't know what i may read about this. I read FAROO Return Values doc, but i don't understand how i may get result-array (or dictionary) in cocoa.
Please anybody give me example or tutorial how to use Faroo API (or other web API) in objective-c.
Thank you.
To use web API and FAROO API in particular i use NSURLConnection class and NSURLConnectionDelegate protocol:
- (IBAction)search:(id)sender {
NSString* requestString = [NSString stringWithFormat:#"http://www.faroo.com/api?q=%#&start=1&length=10&l=ru&src=news&f=xml&YOUR_API_KEY",[searchField stringValue]];
// NSLog(#"str %#",requestString);
NSURL* requestUrl = [NSURL URLWithString:requestString];
NSURLRequest* searchRequest = [NSURLRequest requestWithURL:requestUrl cachePolicy:NSURLRequestReloadRevalidatingCacheData timeoutInterval:60];
[self performSelectorOnMainThread:#selector(startConnectionWithRequest:) withObject:searchRequest waitUntilDone:NO];
}
- (void)startConnectionWithRequest:(NSURLRequest*)request {
NSURLConnection* connection = [[NSURLConnection alloc]initWithRequest:request delegate:self startImmediately:YES];
if (connection) {
//update GUI and do something...
theData = [NSMutableData data];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"Receive data");
[theData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
NSLog(#"Http status code %ld",(long)[httpResponse statusCode]);
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Finish");
//do something with data and update GUI
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSAlert* searchFailedAlert = [NSAlert alertWithError:error];
[searchFailedAlert runModal];
}
One other way of doing things is to declare the missing method yourself as a category of the class in question. This will get the compiler to stop complaining about not finding the method, though of course you'll still need the runtime check you're already doing to avoid actually calling the method. You might also want to wrap such a declaration using availability macros, so that it will be ignored once you do move up to using the 10.5/10.6 SDK and you won't get a different compiler complaint down the line. That would look something like this:
#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_4 //ignore when compiling with the 10.5 SDK or higher
#interface NSPropertyListSerialization(MissingMethods)
+ (NSData *)dataWithPropertyList:(id)plist format:(NSPropertyListFormat)format options:(NSPropertyListWriteOptions)opt error:(NSError **)error;
#end
#endif
I am relatively new to objective-c but struggling with delegates when it comes to NSURLConnection. Below I have an implementation file api.m
Elsewhere in my viewcontrollers I call this api object with the method getGroups and the purpose here is to return the number of groups found when the API request is made. I can see the data in the didReceiveData but how can I get this data back into my getGroups so that I can access it in my viewController?
In my view controller I have something like:
NSInteger *numGroups = [apiRequest getGroups];
and in my api.m implementation file I have the following. Again everything works I am just not sure how to return the data from didReceiveData back so I can access it in getGroups method.
#import "API.h"
#import "Constants.h"
#import "JSONParser.h"
#implementation API
#synthesize user, url, receivedData
-(NSInteger)getGroups {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10];
[request setValue:APIKEY forHTTPHeaderField:#"apikey"];
[request setHTTPMethod:#"GET"];
[request setURL:url];
NSURLConnection *myConnection;
myConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
//How do I access what was append'd in receivedData below
return 2;
}
/* ----------------------------------------------------------------------------------------
NSURLConnection Delegates
------------------------------------------------------------------------------------------- */
// Check the response code that was returned
- (NSInteger)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
return [httpResponse statusCode];
}
// Take a peak at the data returned.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"DATA: %#", [data description]);
//How to get this information back up into the getGroups method
[receivedData appendData: data];
}
// Close the connection
- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
NSLog(#"Connection Closed.");
}
#end
What you want to do is in your ViewController that is calling the API set the API's delegate to self. Then you need to add those delegate methods inside your ViewController, not use them out of the API. That way when the NSURLConnection tries to call one of the delegate methods it will be accessible within youre ViewController. You also want to make sure you add the delegate protocol inside your ViewController's .h file as well.
As a quick example your VC.h file will contain the following:
#interface ViewController : UIViewController <NSURLConnectionDataDelegate>
Then in your VC.m file you'd have the following methods:
/* ----------------------------------------------------------------------------------------
NSURLConnection Delegates
------------------------------------------------------------------------------------------- */
// Check the response code that was returned
- (NSInteger)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
return [httpResponse statusCode];
}
// Take a peak at the data returned.
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"DATA: %#", [data description]);
//How to get this information back up into the getGroups method
[receivedData appendData: data];
}
// Close the connection
- (void)connectionDidFinishLoading:(NSURLConnection*)connection {
NSLog(#"Connection Closed.");
}
Now when your NSURLConnection tries to call didReceiveData it will be called inside your ViewController, not in the API.
As a side note I whole heartedly recommend taking #SK9's advice and make this an Async call to abstract it from the main thread.
NSURLConnection's sendSynchronousRequest:returningResponse:error:, see here, will return a data object. Do be sure you're happy to block the current thread like this. I'd prefer for this to be an asynchronous request, with a completion block to handle the return. More details on the page I referred to, but do make reading up on blocks a priority if this is new. The Short Practical Guid to Blocks might help.
I'm working on a project for school, and I was wondering if there was a way to actually update the connection with NSURLConnection if a user submits something. For example, I'm having twitter search based on username, and I have a default username set up when the view loads. What would I have to do to refresh that connection when a user enters a different username on that page? I have my button handlers all set up, and I'm using the same code as in my viewDidLoad function. Is there a method call or something that will actually reset the connection?
Thanks,
David
I attached example codes
// member variable 'conn'
// NSURLConnection *conn;
// #property(strong, nonatomic) NSURLConnection *conn;
-(void)viewDidLoad {
[super viewDidLoad];
[self sendRequest];
}
- (IBAction)sendButtonClicked {
[self sendRequest];
}
- (void)sendRequest {
if (self.conn) {
[self.conn cancel];
self.conn = nil;
}
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]];
NSURLConnection *conn = [NSURLConnection connectionWithRequest:request delegate:self];
self.conn = conn;
[conn start];
}
// NSURLConnection Delegates
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// append received data
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
// error handling
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// using data
}
I don't think you can "reset" the connection. You will have to make a new request. In your button clicked method simply make a new request call with the new user name.
It works like a request in a browser, when you change some parameter in the url the browser sends of a new request to the web-server.
have a look at this example by apple (if you dont need the caching part just focus on URLCacheConnection.h/.m) http://developer.apple.com/library/ios/#samplecode/URLCache/Listings/Classes_URLCacheController_m.html#//apple_ref/doc/uid/DTS40008061-Classes_URLCacheController_m-DontLinkElementID_10
i
Whenever I create an NSURLConnection in a class I have, it always connects to the first URL connected to by that class. It has an ivar conn that the NSURLConnection is stored in, and here is the method that connects:
-(void)getMoreProblems
{
problemsPage++;
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://projecteuler.net/problems;page=%d",problemsPage]];
NSURLRequest *req=[NSURLRequest requestWithURL:url];
NSLog(#"%p",conn);
conn=[[NSURLConnection alloc] initWithRequest:req delegate:self];
NSLog(#"%p",conn);
}
I have checked by NSLoging the URL's description and the Connection's pointer that they are different, as well as telling the UIApplication to load the URL in safari. As far as I can tell, It tries to load the right page. I also tried both POST and GET, but it didn't make a difference. What might be causing this?
EDIT FOR ANYONE LOOKING AT THIS WITH A SIMILAR PROBLEM:
My problem ended up being that I did not reinitialize the NSMutableData I stored the connection data in after each page loaded.
This isn't really an answer, but it's too long for a comment. I can't see anything wrong with the code that you posted. I pasted your code for getMoreProblems into a new project and added the delegate methods necessary to look at the results -- as far as I can tell it worked fine. I can see in the resulting string, the problem numbers starting with 1 on the first page I receive (from the first call to getMoreProblems) and starting with problem 51 on the second call to getMoreProblems. The only thing I added to your getMoreProblems method was the if-else clause at the end. HEre is the code I used:
#synthesize window = _window,receivedData;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
problemsPage = 0;
[self getMoreProblems];
}
-(void)getMoreProblems {
problemsPage++;
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://projecteuler.net/problems;page=%d",problemsPage]];
NSURLRequest *req=[NSURLRequest requestWithURL:url];
NSLog(#"%p",conn);
conn=[[NSURLConnection alloc] initWithRequest:req delegate:self];
NSLog(#"%p",conn);
if (conn) {
self.receivedData = [NSMutableData data];
} else {
NSLog(#"The Connection Failed");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"%#",response.URL);
[self.receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"In connection:didReceiveData:");
[self.receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Succeeded! Received %lu bytes of data",[receivedData length]);
NSString *page = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
NSLog(#"%#",page);
[self performSelector:#selector(getMoreProblems) withObject:nil afterDelay:5];
}
So, I can't reproduce your problem -- I'm guessing it lies elsewhere in some code that you didn't post.
I am attempting to write a bit of code that checks the URL of a datasource, then populates an array with objects from that URL. It actually works well, but if there is a problem with the web connection or the address I want to populate the array with data from a bundled file. The issue I am having is that the connection didFailWithError method is never called. I tried passing a simple string but it does not call. I want the app to still function for people who are using ipod touch or are in airplane mode.
connection didReceiveResponse is working without issue.
This is what I'm working with.
- (void)loadListData{
NSLog(#"Loading data from sources");
NSURLRequest *listURLRequest = [NSURLRequest requestWithURL:integerPhoneListURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:1.0];
[[NSURLConnection alloc] initWithRequest:listURLRequest delegate:self];
if (!listConnectFail){
phoneListJSON =[NSData dataWithContentsOfURL:integerPhoneListURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:phoneListJSON waitUntilDone:YES];
} else {
//This will tell us if there is an error loading the file
NSLog(#"File not found on web init from file");
phoneListJSON =[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"contactlist" ofType:#"json"]];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:phoneListJSON waitUntilDone:YES];
}
//Initialize the filtered list with array of customer objects. Based on original data
filteredList = [[NSMutableArray alloc] init];
for (NSDictionary *dict in phoneListOriginal) {
contact *single = [[contact alloc] init];
single.fName = [dict objectForKey:#"fName"];
single.lName = [dict objectForKey:#"lName"];
single.extension = [dict objectForKey:#"extension"];
single.title = [dict objectForKey:#"title"];
single.department = [dict objectForKey:#"department"];
single.cellNumber = [dict objectForKey:#"cellNumber"];
//NSLog(#"%#", single.lName);
[filteredList addObject:single];
}
NSLog(#"Array filteredLIst contains %d records",[filteredList count]); }
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
listConnectFail = YES;
NSLog(#"Connection Failed, pulling from file"); }
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
listConnectFail = NO;
NSLog(#"Connection Succeeded, populating from API");
}
I know it is probably something stupid that I am not seeing, but I could use the help to see what I don't
Thanks in advance!
How did you confirm that your delegate did not receive the message? Did you check the log?
Your code seems to assume that 'listConnectFail' will be set immediately after the NSURLConnection's init is done, which is not necessarily the case.
[[NSURLConnection alloc] initWithRequest:listURLRequest delegate:self];
if (!listConnectFail){...}
The NSURLConnection documentation states that 'The delegate will receive delegate messages as the load progresses.'
However, I am not sure about the airplane mode, maybe this particular error can be detected synchronously.