I'm using AFNetworking to get JSON data loaded like this which is working great.
SessionManager *sessionManager = [SessionManager sharedClient];
[sessionManager setResponseSerializer:[AFJSONResponseSerializer serializer]];
[sessionManager setRequestSerializer:[AFJSONRequestSerializer serializer]];
[sessionManager GET:urlString
parameters:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
self.navigationItem.titleView = nil;
self.data = [responseObject objectForKey:#"courses"];
self.title = responseObject[#"meta"][#"ref_title"];
[self.tableView reloadData];
}
failure:^(NSURLSessionDataTask *task, NSError *error) {
NSString *errorString = [NSString stringWithFormat:#"%#",
[error localizedDescription]];
[aiView stopAnimating];
}];
The problem is that I don't want to be loading the data every time.
So I used
sessionManager.requestSerializer.cachePolicy = NSURLRequestReturnCacheDataElseLoad;
This allows faster user experience, but now the problem is that the data is never deleted, or is it? So the user might not realize there's newer data available.
For this I use at the moment NSTimer, which starts when app is loaded and then calls
[[NSURLCache sharedURLCache] removeAllCachedResponses];
I think this is pretty bad solution as the timer will call the method even if the data was loaded 5 seconds ago. So I ask is there anything better?
I've seen a similar question here, but it's about clearing image cache and there's nothing about the age of the cache. Best case for me would be if the cache could be deleted when the day changes.
Have you looked into other caching policies? NSURLRequestReloadRevalidatingCacheData looks like it might be more suitable for your needs. It will only use cache data if the origin source confirms that the data is still valid.
Well I decided to make a date check in application didFinishLaunchingWithOptions and saving the date to NSUserDefaults. So for me I set it so that if the dates don't match I clear all the cache and the stored values in NSUserDefaults.
If I understand the code correctly it should only clear everything if user launches the app on the next day, which is perfect for me.
NSDate *dateCheck = [NSDate date];
NSDate *saveDate = [[NSUserDefaults standardUserDefaults] objectForKey:#"lastStartDate"];
if (!saveDate) {
// This is the 1st run of the app
saveDate = [NSDate date];
[[NSUserDefaults standardUserDefaults] setObject:saveDate forKey:#"lastStartDate"];
}
NSLog(#"First run was on: %#", saveDate);
NSInteger interval = [[[NSCalendar currentCalendar] components:NSCalendarUnitDay
fromDate:saveDate
toDate:dateCheck
options:0] day];
if (interval < 0 || interval > 0) {
// Dates don't match lets remove cache and delete saveDate
[self removeAllCachedResponses];
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
} else {
// Dates are equal no need to do anything
NSLog(#"dates are same");
}
As you can see I delete everything from NSUserDefaults, as I'm not using it for anything else. You might want to change that for you.
EDIT:
Well day changed, so I decided to check if this actually works, and the answer is no. Here's my log
2014-09-10 16:00:30.626 SodexoMenuApp[4283:607] datecheck: 2014-09-10 21:00:30 +0000
2014-09-10 16:00:30.627 SodexoMenuApp[4283:607] First run was on: 2014-09-09 23:02:02 +0000
So something has to be changed. Any ideas?
EDIT 2:
I think I solved this. Just had to change components:NSCalendarUnitDay
Related
I have string coming from webservice and I need to fetch value from HTML tag.
<b>Time:</b> Sunday, 17 July 2016 at 18:00<br /><b>Details:</b> Plug in to a regular feast of glorious gospel revelation! / Visit www.TheNewMystics.TV to become a premium member. Each month, John Crowder interacts live with viewers, answering questions, teaching and releasing the glory via live stream. / Members can send in questions live, enjoy each month's broadcast and have access to all archive teachings. / / Starts at 6 p.m. Pacific/ 9 p.m. Eastern<br /><b>Location:</b> www.thenewmystics.tv
How can I get value of Time, location and details from HTML string
Thanks
You can use substringWithRange. Pass HTML tag between two range you will get string between those.
NSRange r1 = [s rangeOfString:#"<b>Time:</b>"];
NSRange r2 = [s rangeOfString:#"<br />"];
NSRange rSub = NSMakeRange(r1.location + r1.length, r2.location - r1.location - r1.length);
NSString *sub = [s substringWithRange:rSub];
You might can use hpple!
such as:
#import "TFHpple.h"
NSData * data = [NSData dataWithContentsOfFile:#"index.html"];
TFHpple * doc = [[TFHpple alloc] initWithHTMLData:data];
NSArray * elements = [doc search:#"//a[#class='sponsor']"];
TFHppleElement * element = [elements objectAtIndex:0];
[e text]; // The text inside the HTML element (the content of the first text node)
[e tagName]; // "a"
[e attributes]; // NSDictionary of href, class, id, etc.
[e objectForKey:#"href"]; // Easy access to single attribute
[e firstChildWithTagName:#"b"]; // The first "b" child node
After playing around with a few different ways to pull website data I developed this simple and quick solution that appears to work well:
int zip = 13153;
int lowerBound = 10000;
int upperBound = 99999;
bool foundValidZip;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
while (foundValidZip == false) {
zip = lowerBound + arc4random() % (upperBound - lowerBound);
// Do any additional setup after loading the view, typically from a nib.
NSString *urString = [NSString stringWithFormat:#"http://www.zip-info.com/cgi-local/zipsrch.exe?zip=%i&Go=Go",zip];
NSURL *URL = [NSURL URLWithString:urString];
NSData *data = [NSData dataWithContentsOfURL:URL];
// Assuming data is in UTF8.
NSString *html = [NSString stringWithUTF8String:[data bytes]];
NSLog(#"%#",html);
NSMutableArray *names = [self stringsBetweenString:#"</th></tr><tr><td align=center>" andString:#"</font></td>" andText:html];
NSMutableArray *states = [self stringsBetweenString:#"</font></td><td align=center>" andString:#"</font></td><td align=center>" andText:html];
if ([names count] > 0 && [states count] > 0) {
NSString *name = [names objectAtIndex:0];
NSString *state = [states objectAtIndex:0];
self.nameLabel.text = name;
self.stateLabel.text = state;
self.zipLabel.text = [NSString stringWithFormat:#"%i",zip];
foundValidZip = true;
}
else {
foundValidZip = false;
}
}
}
-(NSMutableArray*)stringsBetweenString:(NSString*)start andString:(NSString*)end andText:(NSString*)text {
NSMutableArray* strings = [NSMutableArray arrayWithCapacity:0];
NSRange startRange = [text rangeOfString:start];
for( ;; )
{
if (startRange.location != NSNotFound)
{
NSRange targetRange;
targetRange.location = startRange.location + startRange.length;
targetRange.length = [text length] - targetRange.location;
NSRange endRange = [text rangeOfString:end options:0 range:targetRange];
if (endRange.location != NSNotFound)
{
targetRange.length = endRange.location - targetRange.location;
[strings addObject:[text substringWithRange:targetRange]];
NSRange restOfString;
restOfString.location = endRange.location + endRange.length;
restOfString.length = [text length] - restOfString.location;
startRange = [text rangeOfString:start options:0 range:restOfString];
}
else
{
break;
}
}
else
{
break;
}
}
NSLog(#"%#",strings);
return strings;
}
Essentially what this is doing is querying a website that looks up the city that a ZIP codes are associated with, then fetching the HTML for a random ZIP code. The program then extracts specific bits of information from that HTML data by searching for text between a unique set of front and end "caps". I've used this "cap" method for a few other sample applications. Some of these do not actually query the website, but fetch data off of a static URL that is updated frequently. One of the only pitfalls I can see here is that if the HTML changes, this may not work. But other than that, it seems to work really well and is extremely quick. Before I publish any of my applications, I want to ensure that a large amount of queries will not damage the websites, or other disadvantages for both me and the webmaster. Is this OK to do? And is there a better alternative? (not for this specific purpose - ZIP codes - but just for pulls in general)
What you're doing is called scraping the web site / page. It's a general approach, but one that isn't ideal and comes with a number of pitfalls...
Generally speaking, you're better off not having any scraping code inside your app, because your app will take quite a while to change and redeploy to the store if the website changes and you need to update.
So, it's best to either have a server of your own do the scraping and then provide your 'sanitised' version of the data to the app, or to use a reconfigurable 3rd party service (like Kimono, I've never used it but the website is colourful) to abstract your app from the nitty gritty.
As for the users, your app / service is just like a normal user, so the website needs to be able to handle the number of users in general.
I agree with the comment from #paulw11 about legality if you don't own / have a relationship with the website involved - you should have a relationship with them...
I am using the following code to convert text to pdf form:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"All_lang_unicode" ofType:#"txt"];
NSString *str;
NSData *myData = [NSData dataWithContentsOfFile:filePath];
if (myData) {
str = [[NSString alloc] initWithData:myData encoding:NSUTF16StringEncoding];
NSLog(#"STRING : %#",str);
}
NSString *html = [NSString stringWithFormat:#"<body>%#</body>",str];
UIMarkupTextPrintFormatter *fmt = [[UIMarkupTextPrintFormatter alloc]
initWithMarkupText:html];
UIPrintPageRenderer *render = [[UIPrintPageRenderer alloc] init];
[render addPrintFormatter:fmt startingAtPageAtIndex:0];
CGRect page;
page.origin.x=0;
page.origin.y=0;
page.size.width=792;
page.size.height=612;
CGRect printable=CGRectInset( page, 0, 0 );
[render setValue:[NSValue valueWithCGRect:page] forKey:#"paperRect"];
[render setValue:[NSValue valueWithCGRect:printable] forKey:#"printableRect"];
NSLog(#"number of pages %d",[render numberOfPages]);
NSMutableData * pdfData = [NSMutableData data];
UIGraphicsBeginPDFContextToData( pdfData, CGRectZero, nil );
for (NSInteger i=0; i < [render numberOfPages]; i++)
{
UIGraphicsBeginPDFPage();
CGRect bounds = UIGraphicsGetPDFContextBounds();
[render drawPageAtIndex:i inRect:bounds];
}
UIGraphicsEndPDFContext();
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString * pdfFile = [documentsDirectory stringByAppendingPathComponent:#"test.pdf"];
[pdfData writeToFile:pdfFile atomically:YES];
But problem is that I am not getting the proper formatting of the text. when I print using NSLog(); I get the proper content but when I place the string in STRING the spacing and newline is missing.. all coming in same line. i.e. continuous.
(UPDATE : )
NSLog OUTPUT:(Proper)
NEW DELHI: Sachin Tendulkar's streak of low scores might have raised a question mark over his future but senior BCCI official and IPL chairman Rajiv Shukla on Monday came out in support of the senior batsman saying one needs to look at his "colossal record" before making any comment.
"He will hang up his boots when he thinks it's time for him to go. He does not need any advice on this. Before making a comment on his performance you have to see his colossal record and his past performance," Shukla told reporters outside the Parliament adding that the veteran cricketer will come back strongly in the forthcoming matches.
and Im getting as:
NEW DELHI: Sachin Tendulkar's streak of low scores might have raised a question mark over his future but senior BCCI official and IPL chairman Rajiv Shukla on Monday came out in support of the senior batsman saying one needs to look at his "colossal record" before making any comment. "He will hang up his boots when he thinks it's time for him to go. He does not need any advice on this. Before making a comment on his performance you have to see his colossal record and his past performance," Shukla told reporters outside the Parliament adding that the veteran cricketer will come back strongly in the forthcoming matches.
Can any one please suggest modification in this code so that I can get the proper format.
If I get it right, you should replace your new line characters with <br> or <p>.
Try
str = [str stringByReplacingOccurrencesOfString:#"\n" withString:#"<br>"];
How to detect new lines in Objective-C
Solution of your next question might look like this:
NSArray *words = [str componentsSeparatedByString:#" "];
NSString *line = #"";
NSUInteger maxLineLength = 100;
NSString *resultStr = #"";
for (NSString *word in words) {
if ([line length] + [word length] > maxLineLength) {
resultStr = [resultStr stringByAppendingFormat:#"%#<br>", line];
line = word;
} else {
line = [line stringByAppendingFormat:#" %#", word];
}
}
resultStr = [resultStr stringByAppendingString:line];
Basically I need to parse td(table data) from this html file.I need to get the right xpath.I am using raywenderlich as a model for this task, and here is the code I have so far.
NSURL *tutorialsUrl = [NSURL URLWithString:#"http://example.com/events];
NSData *tutorialsHtmlData = [NSData dataWithContentsOfURL:tutorialsUrl];
// 2
TFHpple *tutorialsParser = [TFHpple hppleWithHTMLData:tutorialsHtmlData];
// 3
NSString *tutorialsXpathQueryString = #"This is where I need to enter my xpath to rerieve the table data";
NSArray *tutorialsNodes = [tutorialsParser searchWithXPathQuery:tutorialsXpathQueryString];
I have the html path to this element thanks to firebug,which I will post below.
/<html lang="en">/<body>/div id="page" class="container">/<div class="span-19">/<div id="content">/<div>/<table id=yw0 class="detail-view">/<tbody>/<tr class="even">/<td>moo</td>/
I need the text moo to be parsed. Any help will be deeply appreciated.
this is the x path I get from firebug as well, but it didn't work at all.
/html/body/div/div[4]/div/div/table/tbody/tr[2]/td
At first, you need to get substrings, where each substring contains one element that needs to be extracted:
NSArray *split = [text componentsSeparatedByString:#"<td>"];
In array "split", first object contains nothing you want, so you will not work with it anymore. Now, for each substring in this array (except first one) you need to search for substring with "/td" tag:
NSRange range = [string rangeOfString:#"</td>"];
and then remove it and everything what is behind it:
- (NSString *)substringToIndex:(NSUInteger)anIndex //you will get index by searching for "</td>" as mentioned
EDIT:
Another possibility is to use componentsSeparatedByString even instead of 2nd and 3rd step for mentioned tag and in first item of each array, you will have wanted text.
EDIT2: (whole code)
NSString* originalText = #" /<html lang=""en"">/<body>/div id=""page"" class=""container"">/<div class=""span-19"">/<div id=""content"">/<div>/<table id=yw0 class=""detail-view"">/<tbody>/<tr class=""even"">/<td>moo1</td><td>moo2</td>/";
NSArray* separatedParts = [originalText componentsSeparatedByString:#"<td>"];
NSMutableArray* arrayOfResults = [[NSMutableArray alloc] init];
for (int i = 1; i < separatedParts.count; i++) {
NSRange range = [[separatedParts objectAtIndex:i] rangeOfString:#"</td>"];
NSString *partialResult = [[separatedParts objectAtIndex:i] substringToIndex:range.location];
[arrayOfResults addObject:partialResult];
}
I have slightly altered original text to show that its really working for table with more items inside
I'm trying to parse an HTML page with a lot of tables. I've searched the net on how to parse HTML with Objective C and I found hpple. I'd look for a tutorial which lead me to:
http://www.raywenderlich.com/14172/how-to-parse-html-on-ios
With this tutorial I tried to parse some forum news which has a lot of tables from this site (Hebrew): news forum
I tried to parse the news title, but I don't know what to write in my code. Every time I try to reach the path I get, "Nodes was nil."
The code of my latest attempt is:
NSURL *contributorsUrl = [NSURL URLWithString:#"http://rotter.net/cgi-bin/listforum.pl"];
NSData *contributorsHtmlData = [NSData dataWithContentsOfURL:contributorsUrl];
// 2
TFHpple *contributorsParser = [TFHpple hppleWithHTMLData:contributorsHtmlData];
// 3
NSString *contributorsXpathQueryString = #"//body/div/center/center/table[#cellspacing=0]/tbody/tr/td/table[#cellspacing=1]/tbody/tr[#bgcolor='#FDFDFD']/td[#align='right']/font[#class='text15bn']/font[#face='Arial']/a/b";
NSArray *contributorsNodes = [contributorsParser searchWithXPathQuery:contributorsXpathQueryString];
// 4
NSMutableArray *newContributors = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in contributorsNodes) {
// 5
Contributor *contributor = [[Contributor alloc] init];
[newContributors addObject:contributor];
// 6
Could somebody guide me through to getting the titles?
Not sure if that's the option for you, but if desired table have unique id's you could use a messy approach: load that html into UIWebView and get contents via – stringByEvaluatingJavaScriptFromString: like this:
// desired table container's id is "msg"
NSString* value = [webView stringByEvaluatingJavaScriptFromString:#"document.getElementById('msg').innerHTML"];