Im having issues with this code reading some .json files, it works fine on json that can be read via IE like a webpage but wont work on files that the url is tring to download them like a ftp style.
In the end im looking at populating labels with the json info once this works but cant see what around the URLRequest is causing it.
This is what code im testing it with:
// WDViewController.h
#import <UIKit/UIKit.h>
#interface WDViewController : UIViewController{
NSArray *jsonArray;
NSMutableData *data;
}
#property (weak, nonatomic) IBOutlet UILabel *outputLabel;
-(IBAction)WDPlayerButtonUpdate:(id)sender;
#end
// WDViewController.m
#import "WDViewController.h"
#interface WDViewController ()
#end
#implementation WDViewController
#synthesize outputLabel;
-(IBAction)WDPlayerButtonUpdate:(id)sender{
NSURL *url = [NSURL URLWithString:#"http://gooruism.com/feed/json"];
NSURLRequest *data = [NSURLRequest requestWithURL:url];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
data = [[NSMutableData alloc] init];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)theData{
[data appendData:theData];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
jsonArray = [NSJSONSerialization JSONObjectWithData:data options:nil error:nil];
// Loop through jsonArray
for (int i = 0; i< jsonArray.count; i++)
{
outputLabel.text = [[jsonArray objectAtIndex:i] objectForKey:#"889"]; // Set Labels
}
}
#end
The json file and url are just one that I found looking for a solution via Google and are only for testing.
After a lot more Google and learning some more about json,
Ive got the code working.
Ended up being more of a null exemption from empty indexes and not calling objectForKeys correctly, as was leaving strings that errored when then using to populate a label.
Code I finally got to work:
//
// WDViewController.m
// JSON Test
#import "WDViewController.h"
#interface WDViewController ()
#end
#implementation WDViewController
#synthesize testLabel;
-(IBAction)WDPlayerButtonUpdate:(id)sender
{
// Set network activity indicator
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
// Set json url.
NSURL *url = [NSURL URLWithString:#"<url for json>"];
// get the url page into the nsdata object.
NSData *jsonData = [NSData dataWithContentsOfURL:url];
// Read json (from jasondata) and convert to object.
if ( jsonData != nil)
{
NSError *error = nil;
id result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
if (error == nil)
NSLog (#"%#", result);
NSLog(#"Test 1");
// Retrieve array and show in log
NSLog(#"From status index: %#", [result objectForKey:#"status"]);
// Retrieve data and log
NSLog(#"From profile.handle index: %#", [[result objectForKey:#"profile"]objectForKey:#"handle"]);
NSLog(#"Next Test 2"); // Populate label with data handle
yestLabel.text = [[result objectForKey:#"profile"]objectForKey:#"handle"];
NSLog(#"End of Tests");
}
}
#end
So i work with UICollectionView with my JSON. I write this code for parse, but i am not understand for why it's not work. My JSON have ([jsontest2] after key "imageMain").
So i paste my code, please help me:
#property (nonatomic,strong) NSMutableArray *patternImagesArray;
#end
#implementation ViewController
#synthesize patternImagesArray = _patternImagesArray;
NSURLConnection *connection;
NSMutableData *webdata;
Try to get data from JSON:
-(void) viewDidLoad{
_patternImagesArray = [[NSMutableArray alloc] init];
NSURL *url = [NSURL URLWithString:#"site"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
connection = [NSURLConnection connectionWithRequest:request delegate:self];
if (connection) {
webdata = [[NSMutableData alloc]init];
}
}
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webdata setLength:0];
}
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[webdata appendData:data];
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
}
JSON parse:
-(void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSDictionary *allDataDictionary = [NSJSONSerialization JSONObjectWithData:webdata options:0 error:nil];
NSArray *tmp =[allDataDictionary objectForKey:#"jsontest2"];
if (tmp.count>0){
for (NSDictionary *diction in tmp) {
[self.patternImagesArray addObject:diction];
}
NSLog(#"%#", self.patternImagesArray);
}
[self.collectionView reloadData];
}
Some customize things:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
PatternView *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"PatternCell" forIndexPath:indexPath];
NSString *myPatternString = [[self.patternImagesArray objectAtIndex:indexPath.row] valueForKey:#"thumb"];
NSLog(#"myPatternString %#",myPatternString);
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:myPatternString]];
cell.patternImageView.image = [UIImage imageWithData:data];
return cell;
}
-(NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return [self.patternImagesArray count];
}
-(CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
return CGSizeMake(100.0, 100.0);
}
You are abusing the variable "array" for two totally different purposes.
The underlying reason is that you haven't bothered to find reasonable names for variables. "array" is actually a global variable. It's not only accessible from any code within your file, but from any code anywhere in your program. Look at this bit of code:
NSDictionary *allDataDictionary =[NSJSONSerialization JSONObjectWithData:_webdata
options:kNilOptions error:nil];
array =[allDataDictionary objectForKey:#"jsontest2"];
for (array in allDataDictionary) {
}
You first parse the JSON data to allDataDictionary (you don't bother checking for errors). You access one key "jsontest2" from the dictionary and store it into the global variable "array". Whatever was in "array" before is now gone.
Then you have a for-loop, using the same global variable as the loop variable! That's total nonsense. But you also need to realise that this will iterate over the keys in your dictionary, so array is now an NSString and not an array anymore.
After that your code gets worse...
Go through your code line by line. Every line, think about what it is doing. There are many severe problems there.
in part of my app (storyboard) I have a table view with various rows, and when I select a row, an html file should be displayed. Instead when I select a row, I get a blank screen instead.
I thought I needed another view controller after the tableview controller, but I get the same issue whether I have one or not.
In my table controller .m file I have the following code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
articleIndex = indexPath.row;
WebView *selectedArticle = [[WebView alloc] initWithNibName:nil bundle:nil];
[self.navigationController pushViewController:selectedArticle animated:YES];
[selectedArticle release];
That is allowing me to select a row and the screen changes to a blank one, rather than loading the relevant html file.
I have in my resources folder a .h and .m file as follows:
webView.h
#import <UIKit/UIKit.h>
#import "globals.h"
#interface WebView : UIViewController{
NSArray *articleNames;
IBOutlet UIWebView *webView;
}
#property(nonatomic, retain)IBOutlet UIWebView *webView;
#end
webView.m
#import "WebView.h"
#implementation WebView
#synthesize webView;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)didReceiveMemoryWarning
{
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
articleNames = [[NSArray arrayWithObjects:
#"chapter1",
#"chapter2",
#"chapter3",
#"chapter4",
#"chapter5",
#"chapter6",
nil] retain];
NSString *chapterName = [[NSString alloc] initWithString:[articleNames objectAtIndex:articleIndex]];
NSString *path = [[NSBundle mainBundle] pathForResource:chapterName ofType:#"html"];
NSURL *url = [NSURL fileURLWithPath:path];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
[webView loadRequest:urlRequest];
[chapterName release];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return YES;
}
#end
I can't see what I have done wrong here, any advice? I'm not sure how to get the html files to display or where I need to make changes to allow it. If I remove the HTML files from the project the app crashes so it's definitely loading them but for some reason not displaying...
Well first off where is "articleIndex" I see you set it in your tableViewController but it is not webView.h or webView.m. I would suggest first adding in a NSLog
NSURL *url = [NSURL fileURLWithPath:path];
NSLog(#"Article URL:%#", url);
This will let you see if the url is correct first. Second it looks like you connected your UIWebView with interface builder, so make sure that the webView is connect correctly. You can also test the webView buy using:
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString: #"http://www.apple.com"]];
And see if apple.com loads. If it does then you know your issue is with your html files or the paths to them.
I would like to parse a webpage to get the content of a table on that page. However, the table is created by calling document.write() and several javascript functions. i.e. when I load the URL request of that page, the source that I get contains no html tags of that table. Is it possible to get the source of the final page (i.e. the source containing the html tags of the table I want)? And it's very curious that I can view the "final page source" in safari Browser's Developer Tools but not the source of the page.
this is the source of the page the source
You should use implement the NSConnectionDataDelegate protocol and use the NSConnection class. I believe that by the time -(void) connectionDidFinishLoading:(NSURLConnection *)connection is called, the page has finished loading completely.
-(void) requestPage
{
NSString *urlString = #"http://the.page.you.want.com";
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLCacheStorageAllowed timeoutInterval:20.0f];
responseData = [[NSMutableData alloc] init];
connection = [[NSURLConnection connectionWithRequest:request delegate:self] retain];
delegate = target;
}
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
if ([response isKindOfClass:[NSHTTPURLResponse class]])
{
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*) response;
//If you need the response, you can use it here
}
}
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[responseData appendData:data];
}
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
[responseData release];
[connection release];
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection
{
if (connection == adCheckConnection)
{
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
//You've got all the data now
//Do something with your response string
[responseString release];
}
[responseData release];
[connection release];
}
yes, just use view generated source in firefox with the web developer extension or firebug extension
I know that there are some caching classes introduced in the iphone sdk recently, and there is also a TTURLRequest from three20's library that allows you to cache a request to a URL. However, because I am loading the web page in UIWebView by calling UIWebView's loadRequest, those techniques are not really applicable.
Any ideas how I can save a web page so that on next app launch, I don't have to fetch from the web again for the full page? The page itself already have some ajax mechanism that updates parts of itself automatically.
There are a bunch of articles about the way the cache of the UIWebView works and the global feeling is that even if some mechanisms seems to work OK under MacOS X, the same approaches may have curious behavior under iPhone.
HOWEVER, I'm doing it by playing with the global cache that is accessed by any NSURLConnection, UIWebView included. And in my case, it works ;).
What you need to understand is the global flow:
YOU -> loadRequest on a UIWebView
This goes into NSURLCache to ask "is there something cached for this request?":
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request
From that, here's what I do to handle the cache on the disk, on my side, to speed up the load of a UIWebView:
Subclass the NSURLCache and override the get control over the -(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request selector
Reimplement this selector in such a way that if nothing has been written on the FS for this request (no cache), then do the request on your side and store the content on FS. Otherwise, return what has been previously cached.
Create an instance of your subclass and set it to the system so that it is used by your application
Now the code :
MyCache.h
#interface MyCache : NSURLCache {
}
#end
MyCache.m
#implementation MyCache
-(NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSLog(#"CACHE REQUEST S%#", request);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray* tokens = [request.URL.relativePath componentsSeparatedByString:#"/"];
if (tokens==nil) {
NSLog(#"ignoring cache for %#", request);
return nil;
}
NSString* pathWithoutRessourceName=#"";
for (int i=0; i<[tokens count]-1; i++) {
pathWithoutRessourceName = [pathWithoutRessourceName stringByAppendingString:[NSString stringWithFormat:#"%#%#", [tokens objectAtIndex:i], #"/"]];
}
NSString* absolutePath = [NSString stringWithFormat:#"%#%#", documentsDirectory, pathWithoutRessourceName];
NSString* absolutePathWithRessourceName = [NSString stringWithFormat:#"%#%#", documentsDirectory, request.URL.relativePath];
NSString* ressourceName = [absolutePathWithRessourceName stringByReplacingOccurrencesOfString:absolutePath withString:#""];
NSCachedURLResponse* cacheResponse = nil;
//we're only caching .png, .js, .cgz, .jgz
if (
[ressourceName rangeOfString:#".png"].location!=NSNotFound ||
[ressourceName rangeOfString:#".js"].location!=NSNotFound ||
[ressourceName rangeOfString:#".cgz"].location!=NSNotFound ||
[ressourceName rangeOfString:#".jgz"].location!=NSNotFound) {
NSString* storagePath = [NSString stringWithFormat:#"%#/myCache%#", documentsDirectory, request.URL.relativePath];
//this ressource is candidate for cache.
NSData* content;
NSError* error = nil;
//is it already cached ?
if ([[NSFileManager defaultManager] fileExistsAtPath:storagePath]) {
//NSLog(#"CACHE FOUND for %#", request.URL.relativePath);
content = [[NSData dataWithContentsOfFile:storagePath] retain];
NSURLResponse* response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:#"" expectedContentLength:[content length] textEncodingName:nil];
cacheResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:content];
} else {
//trick here : if no cache, populate it asynchronously and return nil
[NSThread detachNewThreadSelector:#selector(populateCacheFor:) toTarget:self withObject:request];
}
} else {
NSLog(#"ignoring cache for %#", request);
}
return cacheResponse;
}
-(void)populateCacheFor:(NSURLRequest*)request {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
//NSLog(#"PATH S%#", paths);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSArray* tokens = [request.URL.relativePath componentsSeparatedByString:#"/"];
NSString* pathWithoutRessourceName=#"";
for (int i=0; i<[tokens count]-1; i++) {
pathWithoutRessourceName = [pathWithoutRessourceName stringByAppendingString:[NSString stringWithFormat:#"%#%#", [tokens objectAtIndex:i], #"/"]];
}
NSString* absolutePath = [NSString stringWithFormat:#"%#/myCache%#", documentsDirectory, pathWithoutRessourceName];
//NSString* absolutePathWithRessourceName = [NSString stringWithFormat:#"%#%#", documentsDirectory, request.URL.relativePath];
//NSString* ressourceName = [absolutePathWithRessourceName stringByReplacingOccurrencesOfString:absolutePath withString:#""];
NSString* storagePath = [NSString stringWithFormat:#"%#/myCache%#", documentsDirectory, request.URL.relativePath];
NSData* content;
NSError* error = nil;
NSCachedURLResponse* cacheResponse = nil;
NSLog(#"NO CACHE FOUND for %#", request.URL);
//NSLog(#"retrieving content (timeout=%f) for %# ...", [request timeoutInterval], request.URL);
content = [NSData dataWithContentsOfURL:request.URL options:1 error:&error];
//NSLog(#"content retrieved for %# / error:%#", request.URL, error);
if (error!=nil) {
NSLog(#"ERROR %# info:%#", error, error.userInfo);
NSLog(#"Cache not populated for %#", request.URL);
} else {
NSURLResponse* response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:#"" expectedContentLength:[content length] textEncodingName:nil];
cacheResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:content];
//the store is invoked automatically.
[[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:YES attributes:nil error:&error];
BOOL ok;// = [[NSFileManager defaultManager] createDirectoryAtPath:absolutePath withIntermediateDirectories:YES attributes:nil error:&error];
ok = [content writeToFile:storagePath atomically:YES];
NSLog(#"Caching %# : %#", storagePath , ok?#"OK":#"KO");
}
[pool release];
}
#end
And the use of it in your application:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentsDirectory = [paths objectAtIndex:0];
NSString* diskCachePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory, #"myCache"];
NSError* error;
[[NSFileManager defaultManager] createDirectoryAtPath:diskCachePath withIntermediateDirectories:YES attributes:nil error:&error];
MyCache* cacheMngr = [[MyCache alloc] initWithMemoryCapacity:10000 diskCapacity:100000000 diskPath:diskCachePath];
[NSURLCache setSharedURLCache:cacheMngr];
This code deserves a lot of cleanup.. but the main things should be in there. I had a lot of trouble to get this working, hope this helps.
I recently found this project under github :
http://github.com/rs/SDURLCache
The approach is quite the same as my previous answer described here How to save the content in UIWebView for faster loading on next launch? , but the code looks more polished so maybe it makes sense to give it a try.
If the page has AJAX already, why not store the JavaScript/HTML in the application bundle to start rather than downloading it on the first launch? Then load the page with the code Corey gave below and let the AJAX handle hitting the network for the updated parts of the page.
Take a look at: http://allseeing-i.com/ASIHTTPRequest/ASIWebPageRequest
You can save an HTML in the documents directory and load the page directly from the documents directory on launch.
To save the webview content:
Reading HTML content from a UIWebView
To load:
NSString* path = [[NSBundle mainBundle] pathForResource:#"about" ofType:#"html"];
NSURL* url = [NSURL fileURLWithPath:path];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];