NSMutableURLRequest and JSON - How would I finish this connection? - ios

I am trying to fetch data from an api and I have this code:
// create the URL we'd like to query
NSString *urlString = [NSString stringWithFormat:#"https://www.googleapis.com/adsense/v1.1/reports"];
NSString *token = auth.accessToken;
NSMutableURLRequest *myURL = [NSURL URLWithString:urlString];
[myURL addValue:token forHTTPHeaderField:#"Authentication"];
// we'll receive raw data so we'll create an NSData Object with it
######
How would I complete this step? So far I have
NSData *myData =
######
// now we'll parse our data using NSJSONSerialization
id myJSON = [NSJSONSerialization JSONObjectWithData:myData options:NSJSONReadingMutableContainers error:nil];
// typecast an array and list its contents
NSArray *jsonArray = (NSArray *)myJSON;
NSLog(#"%#", jsonArray);
How would I connect on the 9th line?

It looks like you are putting all this code in one place (making a synchronous network request). This is typically a bad idea. You should put the first part (creating and starting the request) in one place, and put the parsing code in a separate method / block that gets called once the request is completed. (This is called an asynchronous network request.)
You should take a look at +[NSURLConnection sendAsynchronousRequest:queue:completionHandler:]. You can pass your NSURLRequest in here, and then specify the completion (parsing, etc.) in the completion handler. It will give you the NSData object you're looking for - see documentation here.
If you have more than a handful of network connections, this can get unwieldy. You may want to look at a third party library like AFNetworking, which can manage much of the tedious stuff for you, and let you focus on calling a URL, and parsing the response.

You need:
NSData *myData = [NSURLConnection sendSynchronousRequest:myURL returningResponse:nil error:nil];
This is a synchronous call. I'll suggest always using asynchronous webcalls is a good way.

Related

NSData dataWithContentsOfUrl: loads outdated data

In my app I load some static JSON string from some server.
Every now and then the JSON file is updated and then I want the app to reload the data.
Now, that I updated the file on the server the app does not reflect the change. If I take the URL to that file from the app's code and copy it into a browser and fetch the file there, I clearly see the updates. But when I run the app and log the json string to the debug console, then I clearly see an outdated version of the file's content.
Is there any caching involved? Can I force the iOS to actually reload it?
This is how I load it now:
NSURL * url = [NSURL URLWithString:[DOWNLOAD_URL stringByAppendingString:DOWNLOAD_FILE]];
NSError * error;
NSData *jsonData = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error];
NSLog(#"JSON: %#", [NSString stringWithUTF8String:[jsonData bytes]]);
The option NSDataReadingUncached should prevent the system from caching the data.
PS: When I run the app on a different device, then it receives the current data. But when I again let it run on the original device - on which I observe this behaviour - then the data "received" is still outdated. So it really looks like some cashing issue to me.
Here is an idea. Try calling
[[NSURLCache sharedURLCache] removeAllCachedResponses];
For more granular control on cashing use NSURLConnection or NSURLSession.
I did try Mundi's suggestion, to try clearing the cache, but this didn't make any difference in my iPhone app.
So, I tried a trick which I use in my Angular webapps, and appended the current time (in ticks) to the URL I'm attempting to open, and that did work:
NSString* originalURL = #"http://somewebservices/data/1234";
NSString* newURL = [NSString stringWithFormat:#"%#?t=%f", originalURL,
[[NSDate date] timeIntervalSince1970]];
NSLog(#"Loading data from: '%#'", newURL);
NSURL *url = [NSURL URLWithString:newURL];
if (url == nil)
return false;
NSError *error;
NSData* urlData = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error];
(Sigh.)
I'm getting too old for this stuff....

How to differentiate different types of Web Services according to URL?

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.

iOS NSURL queuing mechansim for multiple requests from file

I am very new to iOS development, but I would like to make an app that has two table view controllers (columns): both are a row of images that act as links. The first would be a column of YouTube videos and the second a column of websites. I would like to have all these listed in a file file.txt listed like so: V, http://youtube.com/example W, http://example.com
There would be a long list of those, the V meaning its a video (for the video column) and W for the websites. Now, I understand how to being the single file in, but what happens afterwards is my concern. Can I read each line into some sort of queue and then fire the NSURL request for each one consecutively? How can that be done with NSURL? Is there perhaps a better approach?
There are two questions for me:
Is a text file really the best format?
I might suggest a plist or archive (if the file is only going to exist only in your app's bundle and/or documents folder) or JSON (if it's going to live on a server before delivering it to the user) instead of a text file. It will make it easier to parse this file than a text file. For example, consider the following dictionary:
NSDictionary *dictionary = #{#"videos" : #[#"http://youtube.com/abc", #"http://vimeo.com/xyz"],
#"websites": #[#"http://apple.com", #"http://microsoft.com"]};
You can save that to a plist with:
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"files.plist"];
[dictionary writeToFile:plistPath atomically:YES];
You can add that file to your bundle or whatever, and then read it at a future date with:
dictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath];
You can, alternatively, write that to a JSON file with:
NSError *error = nil;
NSData *data = [NSJSONSerialization dataWithJSONObject:dictionary options:NSJSONWritingPrettyPrinted error:&error];
NSString *jsonPath = [documentsPath stringByAppendingPathComponent:#"files.json"];
[data writeToFile:jsonPath atomically:YES];
You can read that JSON file with:
data = [NSData dataWithContentsOfFile:jsonPath];
dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
Either way, you can get the list of videos or web sites like so:
NSArray *videos = dictionary[#"videos"];
NSArray *websites = dictionary[#"websites"];
Now that you have your arrays of videos and websites, the question then is how you then use those URLs.
You could do something like:
for (NSString *urlString in videos) {
NSURL *url = [NSURL URLWithString: urlString];
// now do something with the URL
}
The big question is what is the "do something" logic. Because you're dealing with a lot of URLs, you would want to use a NSOperation based solution, not a GCD solution, because NSOperationQueue lets you control the degree of concurrency. I'd suggest a NSOperation-based networking library like AFNetworking. For example, to download the HTML for your websites:
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 4;
for (NSString *urlString in websites)
{
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// convert the `NSData` responseObject to a string, if you want
NSString *string = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
// now do something with it, like saving it in a cache or persistent storage
// I'll just log it
NSLog(#"responseObject string = %#", string);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error = %#", error);
}];
[queue addOperation:operation];
}
Having said that, I'm not sure it makes sense to kick off a ton of network requests. Wouldn't you really prefer to wait until the user taps on one of those cells before retrieving it (and for example, then just open that URL in a UIWebView)? You don't want an app that unnecessarily chews up the user's data plan and battery retrieving stuff that they might not want to retrieve. (Apple has rejected apps that request too much data from a cellular connection.) Or, at the very least, if you want to retrieve stuff up front, only retrieve stuff as you need it (e.g. in cellForRowAtIndexPath), which will retrieve the visible rows, rather than the hundreds of rows that might be in your text/plist/json file.
Frankly, we need a clearer articulation of what you're trying to do, and we might be able to help you with more concise counsel.

NSURLConnection Data broken in asynchronous connection

I have a NSURLConnection that receives data output from a url pointing to a php script on my server.
Most of the time everything works fine and the data is retrieved in its complete form.
However, sometimes I receive NULL or broken (i.e. the bottom half) of data at:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
When this happens, if I reload the connection it will always return the same null or broken block
of data for the request.
EDIT*:
I've realized that when I receive what I thought was nil data, I actually received data
but the NSString created from this data is nil. I still don't understand why though. My php encoding output is always UTF-8 so I don't think it is an issue of encoding and besides it works most of the time with this.
I have checked the php script with that same request to verify that it is not a problem on the server side or with the php script and confirmed that it is NOT.
My code is Below:
-(void)setUpConnectionAndMakeRequest {
NSString *URLpath = #"http://www.example.com/myphp.php";
NSURL *myURL = [[NSURL alloc] initWithString:URLpath];
NSMutableURLRequest *myURLRequest = [NSMutableURLRequest requestWithURL:myURL cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:60];
[myURL release];
[myURLRequest setHTTPMethod:#"POST"];
//I added this because I thought it may be a problem relating to cache but it isn't
NSURLCache *cache = [NSURLCache sharedURLCache];
[cache removeAllCachedResponses];
NSString *httpBodystr = [NSString stringWithString:#"command=runscript"];
[myURLRequest setHTTPBody:[httpBodystr dataUsingEncoding:NSUTF8StringEncoding]];
[mailData setData:nil]; //mailData is a NSMutableData object which accumulates the data retrieved by the request
[NSURLConnection connectionWithRequest:myURLRequest delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSString *untrimmedDataSTR = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; //Created so I can see the data (always text) in NSLog
NSLog(#"Live Data: %#", untrimmedDataSTR); //This is where I see that the data is broken or null when it shouldn't be
[mailData appendData:data]; //Append accumulated data to NSMutableData object used later in my app.
[untrimmedDataSTR release];
}
Any help would be much appreciated.
According to the NSString reference, -initWithData:encoding: returns nil if "the initialization fails for some reason (for example if data does not represent valid data for encoding)."
That almost certainly means that the response from the server is not, in fact, UTF-8 encoded data.
The way to check would be to NSLog the data before trying to convert to an NSString:
NSLog(#"Raw Data: %#", data);
(The -description method on NSData will return a hexadecimal representation of the contents; that's what will get logged).

NSMutableArray writeToUrl

Is it possible to use the:
[NSMutableArray writeToURL:(NSString *)path atomically:(BOOL)AuxSomething];
In order to send a file (NSMutableArray) XML file to a url, and update the url to contain that file?
for example:
I have an array and I want to upload it to a specific URL and the next time the app launches I want to download that array.
NSMutableArray *arrayToWrite = [[NSMutableArray alloc] initWithObjects:#"One",#"Two",nil];
[arrayToWrite writeToURL:
[NSURL urlWithString:#"mywebsite.atwebpages.com/myArray.plist"] atomically:YES];
And at runtime:
NSMutableArray *arrayToRead =
[[NSMutableArray alloc] initWithContentsOfURL:[NSURL urlWithString:#"mywebsite.atwebpages.com/myArray.plist"]];
Meaning, I want to write an NSMutableArray to a URL, which is on a web hosting service (e.g. batcave.net, the URL receives the information and updates server sided files accordingly.
A highscore like setup, user sends his scores, the server updates it's files, other users download the highscores at runtime.
As for part one of your question,
I'll assume you want to use the contents of a NSMutableArray to form some sort of a URL request (like POST) that you will send to your web service and expect back some information...
There is no prebuilt way of sending the contents of a NSMutableArray to an URL but there are simple ways of doing this yourself. For example, you can loop through the data of your array and make use of NSURLRequest to create a URL request that complies with the interface of your web service. Once you've constructed your request you can send it by passing it a NSURLConnection object.
Consider this very simple and incomplete example of what the client-side code might look like using an Obj-C array to provide data...
NSMutableData *dataReceived; // Assume exists and is initialized
NSURLConnection *myConnection;
- (void)startRequest{
NSLog(#"Start");
NSString *baseURLAddress = #"http://en.wikipedia.org/wiki/";
// This is the array we'll use to help make the URL request
NSArray *names = [NSArray arrayWithObjects: #"Jonny_Appleseed",nil];
NSString *completeURLAsString = [baseURLAddress stringByAppendingString: [names objectAtIndex:0]];
//NSURLRequest needs a NSURL Object
NSURL *completeURL = [NSURL URLWithString: completeURLAsString];
NSURLRequest *myURLRequest = [NSURLRequest requestWithURL: completeURL];
// self is the delegate, this means that this object will hanlde
// call-backs as the data transmission from the web server progresses
myConnection = [[NSURLConnection alloc] initWithRequest:myURLRequest delegate: self startImmediately:YES];
}
// This is called automatically when there is new data from the web server,
// we collect the server response and save it
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"Got some");
[dataReceived appendData: data];
}
// This is called automatically when transmission of data is complete
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
// You now have whatever the server sent...
}
To tackle part 2 of your question, the receiver of a web request will likely require some scripting or infrastructure to make a useful response.
Here, Answer in this question:
Creating a highscore like system, iPhone side
I couldn't edit my post because I posted from my iPhone as an anonymous user, sorry.

Resources