I've spent about 16 hours researching and attempting different code changes, but cannot figure this one out. I have an iOS app that consumes a website using ASIHTTPrequest:
-(void)refresh{
NSURL *url = [NSURL URLWithString#"http://undignified.podbean.com/"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request startSynchronous];
NSString *response = [request responseString];
NSLog(#"%#",response];
}
The above code returns the website source and spits it out to the console via NSLog. The goal I'm trying to achieve from this is search through the 'responseString' for URLs ending in *.mp3, load those into an array and finally load the mp3 URLs into a UITableView.
To summarize:
Consuming website data with ASIHTTPRequest
Trying to search through the responseString for all links that have *.mp3 extensions and load them into an array.
Add parsed links to UITableView.
I think at this junction I have attempted too many things to make any sound judgements at this point. Any suggestions or nudges in the right direction would be greatly appreciative.
Below is an example of the response (HTML). Please note this is only a snippet as the entire HTML source is rather large, but this includes a section to the mp3 files:
a href="http://undignified.podbean.com/mf/web/ih2x8r/UndignifiedShow01.mp3" target="new"><img src="http://www.podbean.com/wp-content/plugins/podpress/images/audio_mp3_button.png" border="0" align="top" class="podPress_imgicon" alt="icon for podbean" /></a> Standard Podcasts [00:40:24m]: <span id="podPressPlayerSpace_2649518_label_mp3Player_2649518_0">Play Now</span> | Play in Popup | Download | Embeddable Player | <a>Hits (214)</a><br/
I really don't like answers that say "Just don't do it that way." But... Just don't do it that way.
It is possible to extract all of the links to mp3s in the html from the address you posted. But this is almost always the wrong way to approach things, and this case is no exception.
Essentially what it seems you are trying to do is create a podcaster client. You should give some thought as to how others have handled this type of use case before. Generally a podcast will have an associated RSS feed that outlines exactly the data you are looking for, again your podcast is no exception. If one simply navigates to the link supplied in your question and then looks around the page for either "subscribe to podcast" or "RSS" they will find the link that leeds to the RSS feed: http://undignified.podbean.com/feed/. This address leads to the XML which contains the items of the podcasts.
This document, unlike the document returned by your original address, is valid XML meaning it can be parsed with an NSXMLParser. NSXMLParser is very powerful & flexible. But a little hard to get started with. Here is some sample code for an NSXMLParser subclass which acts as it's own delegate.
UnDigParser.h
#import <Foundation/Foundation.h>
#interface UnDigParser : NSXMLParser <NSXMLParserDelegate>
#property (readonly) NSArray *links;
#end
UnDigParser.m
#import "UnDigParser.h"
#implementation UnDigParser{
NSMutableArray *_links;
}
#synthesize links = _links;
-(void)parserDidStartDocument:(NSXMLParser *)parser{
_links = [[NSMutableArray alloc] init];
}
-(void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict{
if ([elementName isEqualToString:#"enclosure"]){
NSString *link = [attributeDict objectForKey:#"url"];
if (link){
[_links addObject:link];
}
}
}
-(BOOL)parse{
self.delegate = self;
return [super parse];
}
#end
This code can be tested like so:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL *url = [NSURL URLWithString:#"http://undignified.podbean.com/feed/"];
// This is a sync call thus the background thread
UnDigParser *parser = [[UnDigParser alloc] initWithContentsOfURL:url];
[parser parse];
NSLog(#"links:%#",parser.links);
});
The logged output was:
links:(
"http://undignified.podbean.com/mf/feed/cfdayc/UndignifiedShow03.mp3",
"http://undignified.podbean.com/mf/feed/wbbpjw/UndignifiedShow02Final.mp3",
"http://undignified.podbean.com/mf/feed/ih2x8r/UndignifiedShow01.mp3",
"http://undignified.podbean.com/mf/feed/t4x54d/UndignifiedShow00.mp3"
)
That should be enough to get you started.
I would do this in two steps:
Get all href values
Use regular expressions to check for a valid url ending in .mp3
BTW, I think ASIHTTPRequest is getting outdated. And if you only use it to fetch HTML, I suggest you look into the build in methods from the iOS framework to do just that.
Using requestDidReceiveResponseHeadersSelector and get file name from header, trying print all key and value from NSDictionary header.
Related
I'm developing an Epub Reader for Ios, notice that i don't use any epub library and i parse the epub myself.
I needed a method for loading resource from epub into the UIWebView, for example images or CSS file ... (resource that requested in the html files).
for this purpose i decided to use NSURLCache class and override it's method (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
so i can intercept the request and find the right data from epub then create a NSCachedURLResponse and return it.
so far so good. BUT the the problem is that the data (i.e images) wont show in the webview. look at below code:
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
{
/*
the request.URL is something like this applewebdata://UUID/OEBPS/Images/cover.jpg ,
so the getResourcePathFromRequest give me the path OEBPS/Images/cover.jpg,
so i can find the right data in epub
*/
NSString *resourcePath = [self getResourcePathFromRequest:request.URL];
/*
ResourceResponse is a class contaning data and mimeType,
i tested this and the data and mimeType are good so this is not the problem
*/
ResourceResponse *response = [[self getBook] fetch:resourcePath];
NSURLResponse *urlResponse = [[NSURLResponse alloc] initWithURL:[request URL] MIMEType:response.mMimeType expectedContentLength:[response.mData length] textEncodingName:#"UTF-8"];
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:urlResponse data:response.mData];
return cachedResponse;
}
at this point everything looks normal, the method getting called, the right data being found and the response object return, but it wont load the image into the webview.
so what is the problem?
tnx in advance.
NOTE: i'm new in ios programming world (coming from android:)
I loaded the data with base url (http://localhost) and everything went fine.
I am getting such error in my iOS application when I am going to open some web pages using buil-in web view engine (based on Safari but Safari works fine):
This page contains the following errors: error on line 2 at column 59:
SystemLiteral " or ' expected. Below is a rendering of the page up to
the first error.
(nothing is rendered).
The funny thing is that these websites can be opened on iPhone's version of my app but not on iPad.
Looking into the Internet I found hundreds of thousands websites which cannot be handled by google robots - when I search in google:
"59: SystemLiteral " or ' expected"
it lists search results but all found websites' descriptions shows my error. However when I open any of them they are rendering fine on desktop browser and iOS Safari.
Anybody knows what may be the reason of that? Non of websites opened from this google search can be rendered in my app on iPad - all shows the same error.
I also tried debug it using Mac's Safari Developer mode but it does not give any hint.
Update: I prepared a minimal project, added UIWebView controller and it shows the same error.
ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIWebView *webView;
#end
ViewController.m:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *fullURL = #"http://catalog.llc.lib.ms.us/polaris/";
NSURL *url = [NSURL URLWithString:fullURL];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[_webView loadRequest:requestObj];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
I also tried few web pages with this code (all from google search I mentioned above), they show also different erros, like "error on line 1077 at column 9: Opening and editing tag mismatch: META line 0 and head." - this comes from http://www.lucindalayton.com/home/?ID=15449 but still they cannot be rendered.
Looking at the response from that web site, it has a blank line at the start of the response, which seemed a little curious. In my first attempt, I trimmed that whitespace:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *fullURL = #"http://catalog.llc.lib.ms.us/polaris/";
NSURL *url = [NSURL URLWithString:fullURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (!data) {
NSLog(#"%s: sendAsynchronousRequest error: %#", __PRETTY_FUNCTION__, connectionError);
return;
}
NSString *html = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
html = [html stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[_webView loadHTMLString:html baseURL:url];
}];
}
But I later noticed that you don't have to trim the whitespace, but rather merely using loadHTMLString seemed to be enough.
I'm unclear as to why Safari handles this properly, as does the iPhone web view, but not the iPad web view. But this seems to get around it.
Rob, thanks from hint but in my case it was something else. I found breakthrough analysing communication between browser and server using some test web page. I will explain it in details:
The problem is related to user agent and mime type.
My application was containing in user agent string, among other things, "Mobile/[somenumber] Safari Version/[someversionnumber]"Some servers are very strict what comes in user agent and for mobile Safari-based browsers are expecting some Safari webkit version so the proper notation should be "Mobile/[somenumber] Safari/[someverionnumber] Version/[someversionnumber]"
Notation affects behavior of the server - it sends different mime type (Content-Type). For websites I mentioned in the qestion when user agent is improper "Content-Type" is set by server to "application/xhtml+xml", but when is proper it is "text/html" which is rendered without any problems. Html code sent in both cases is exactly the same.
Anyway, for me it is still hard to say if it is server site fault, which lets sending "application/xhtml+xml" content type to mobile device, or webkit which should handle such code anyway or very strict user agent notation.
So I have finally created a VERY simple application in which I invoke a web service and NSLog the JSON data. I have used about 3 web services so far, and all of them look different. For example, in the small little app I made I used two different URLS. My code is below:
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *urlString = [NSString stringWithFormat:#"http://ielmo.xtreemhost.com/array.php"];
NSString *urlString2 = [NSString stringWithFormat:#"http://bookapi.bignerdranch.com/courses.json"];
NSURL *url = [NSURL URLWithString:urlString2];
NSData *data = [NSData dataWithContentsOfURL:url];
NSError *error;
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSLog(#"%#", json);
// Do any additional setup after loading the view, typically from a nib.
}
My question is how come I can NSLog BOTH of those link's json data fine even if one of them is a .php url. Sorry for such a nooby question but I want to go about looking for web services I can use, and I want to be able to know how to look, because when I look for my own web services, they don't end up working.
There's no way you can differentiate the content of a URL by the URL itself (unless it's an HTML)
The contents of a URL depend on what the server wants to serve. You can have an http://www..../something.php for example, and there's no way to know what you'll get, it could be an HTML page, it could be a PDF document, it could be a zip file, or it could be JSON content.
I'm an iOS newb (.NET professional), so this may be a simple issue but I couldn't find anything through the SO search or Google (and maybe not looking for the right terms).
I'm writing an app that displays information from a DD-WRT router through it's web interface. I have no problem displaying the initial page and navigating through any of the other pages, but if I make any change on a form (and it redirects to apply.cgi or applyuser.cgi), the UIWebView is blank - it's supposed to display the same page, with the form submission changes. The site works fine in Mobile Safari, which I find intriguing, but I guess UIWebView isn't totally the same.
I think the iOS code is pretty standard for display a webpage, but I'll list it below. I can't give you access to my router because, well, that's not a good idea :) Hopefully someone with a DD-WRT router can help (or know what my issue is anyway).
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *sURL = #"http://user:pass#XXX.XXX.X.X";
NSURL *url = [NSURL URLWithString:sURL];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[self.webView loadRequest:request];
self.webView.delegate = self ;
}
And I'm doing a few things with Javascript in the webViewDidFinishLoad method, but I know that's not the culprit because it still happens when I comment it out.
Well I figured out the problem on my own. I think part of it was putting the username & password in the URL (which was just a temporary measure) because I found that method provided the same results in mobile Safari and desktop Chrome.
So I added MKNetworkKit to my project that provided a simple way to add authentication to my request, and found I had to make a specific request to POST the data, then reloaded the page the to see the changes.
In the (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType method, I check if ([request.HTTPMethod isEqualToString:#"POST"]) and do this:
NSString *sPostData = [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding];
NSArray *aPostData = [sPostData componentsSeparatedByString:#"&"];
NSMutableDictionary *dPostData = [[NSMutableDictionary alloc] init];
//i don't know if this is the best way to set a dictionary, but it works
for (id apd in aPostData)
{
NSString *key = [apd componentsSeparatedByString:#"="][0];
NSString *val = [apd componentsSeparatedByString:#"="][1];
[dPostData setValue:val forKey:key];
}
MKNetworkEngine *engine = [[MKNetworkEngine alloc] init];
MKNetworkOperation *op = [engine operationWithURLString:[request.URL description] params:dPostData httpMethod:#"POST"];
[op setUsername:#"myUserName" password:#"myPassword" basicAuth:YES];
self.postedRequest = TRUE; //a bool I set so, when it comes to webViewDidFinishLoad, I reload the current page
[op start]; //send POST operation
I'm pulling data into a UITableView via an XML feed from a Wordpress site. I wanted to display the table with an image if the post contained one and a default image if it did not. So in my
- (void) parser:(NSXMLParser *)parser didEndElement:(NSString *)elementname namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName
method, I have an if statement that looks like this:
if ([elementname isEqualToString:#"content:encoded"]) {
NSString *firstImageURL = [self getFirstImageUrl:currentStory.content];
currentStory.imageURL = firstImageURL;
UIImage *image = [UIImage imageWithData: [NSData dataWithContentsOfURL:[NSURL URLWithString: firstImageURL]]];
currentStory.image = image;
}
This calls getFirstImageURL, which looks like this:
-(NSString *)getFirstImageUrl: (NSString *) html {
NSScanner *theScanner;
NSString *imageURL = nil;
theScanner = [NSScanner scannerWithString: html];
// find start of tag
[theScanner scanUpToString: #"<img" intoString: NULL];
if ([theScanner isAtEnd] == NO) {
[theScanner scanUpToString: #"src=\"" intoString: NULL];
NSInteger newLoc2 = [theScanner scanLocation] + 5;
[theScanner setScanLocation: newLoc2];
// find end of tag
[theScanner scanUpToString: #"\"" intoString: &imageURL];
}
return imageURL;
}
Everything works as it should, but loading the table takes about 5 to 6 seconds and can sometimes take up to 10 seconds, which is not desirable. I was wondering if there is anything I can do to speed up the process of grabbing the first photo.
UPDATE
So after more investigation, it appears that the bottleneck I'm seeing has nothing to do with me downloading images. In fact the actual downloading of images takes no longer than 2 seconds consistently. It looks like the bottleneck happens when I download the RSS feed:
NSData *data = [[NSData alloc] initWithContentsOfURL:url];
This consistently takes the longest.
2012-03-30 14:35:11.506 gbllc[883:3203] inside grabDataForFeed
2012-03-30 14:35:11.510 gbllc[883:3203] reached loadXMLByURL
2012-03-30 14:35:11.512 gbllc[883:3203] after stories alloc
**** 5 seconds ****
2012-03-30 14:35:16.568 gbllc[883:3203] after initWithContentsOfURL
2012-03-30 14:35:16.570 gbllc[883:3203] after initWithData
2012-03-30 14:35:16.573 gbllc[883:3203] about to parse
*** I now parse the XML and download images, takes 2 seconds ***
2012-03-30 14:35:18.066 gbllc[883:3203] Parsed successfully
Right after I alloc my data object, I grab the data for parsing. So I guess my original question is no longer valid and I should probably ask if there's a faster way to grab the initial data for parsing or if I should change my model and try to use json or something?
That's because you're downloading the image data itself over the network. You need to offload that and do it asynchronously. Have an NSOperationQueue where you can queue up the image download to happen on a separate thread.
Here's a great example of doing just that: http://davidgolightly.blogspot.com/2009/02/asynchronous-image-caching-with-iphone.html
I marked Joel's answer as the best answer because he gave me the idea for async downloading of images, but the solution I ended up using is here:
http://howtomakeiphoneapps.com/how-to-asynchronously-add-web-content-to-uitableview-in-ios/1732/
It's by far the easiest and most elegant I've seen after hours of searching.