NSURLErrorDomain Code -1002 downloading pdf - ios

I'm trying to cache a webpage that I can then later show using a UIWebView.
I have the relevant NSURLSessionDataTask inside a for loop (trying to cache 6 webpages) inside the completion block of another NSURLSessionDataTask. When I run, I keep getting this error:
Ayy there was error downloading, data:<>
response:(null)
error:Error Domain=NSURLErrorDomain Code=-1002 "The operation couldn’t be completed. (NSURLErrorDomain error -1002.)" UserInfo=0xdd89d30 {NSUnderlyingError=0xdd89ba0 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1002.)"}
Here's a snippet of what I'm calling
for (MAClass *class in [myDictResult objectForKey:#"classes"]) {
NSString *PRURL = [[[class assignments] objectAtIndex:[[class assignments] count]-1] assignmentName];
NSLog(#"PRURL is %#", PRURL);
NSURLSessionDataTask *progressReportTask = [defaultSession dataTaskWithURL:[NSURL URLWithString:PRURL] completionHandler:^(NSData *progressReportData, NSURLResponse *progressReportResponse, NSError *progressReportError) {
if ([progressReportData length] > 0 && progressReportError == nil) {
NSLog(#"got dat data");
} else NSLog(#"Error with getting data data:%#\nresponse:%#\nerror:%#", progressReportData, progressReportResponse, progressReportError);
}];
[progressReportTask resume];
NSLog(#"After request");
}
I've made sure that the URL is valid, seeing that was the cause for other people with getting the same error (my urls are like https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/PrintProgressReport/20152193^HS4, which are valid when I put them into a browser)
What am I doing wrong?

-1002 is NSURLErrorUnsupportedURL/kCFURLErrorUnsupportedURL. In the future, you can either search the Xcode documentation for NSURLErrorDomain or use quick open (shift+command+O) to browser the headers for the definition of NSURLErrorDomain. Either technique would have lead you to discover that -1002 in NSURLErrorDomain is NSURLErrorUnsupportedURL.
The reason for this error is that your URL contains some characters that have to be percent escaped. And web browsers will frequently do the necessary percent-escaping for you, which is why it works there.
You can use stringByAddingPercentEscapesUsingEncoding to convert the URL to an acceptable format:
NSString *urlString = #"https://mistar.oakland.k12.mi.us/novi/StudentPortal/Home/PrintProgressReport/20152193^HS4";
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionTask *task = [defaultSession dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
...
}];
By the way, when reconciling web browser results against the app, Charles is very useful. Run the request from browser and again from the app and compare the results in Charles. If you had compared these, you would have seen that you needed to percent escape the URL.
By the way, you can also refer to section 2 of RFC 3986 for a technical description of what characters in URLs must be percent escaped.

If your urlString contains a query string, also consider using NSURLQueryItem to build the queryString.
It will create the URL in its acceptable format.
Here is an example of how to put it into use: Building URLs with NSURLQueryItems and NSURLComponents.

Related

How to get Meta tag from URL in swift 3

We are developing chat base app. In which we want to show meta tag info like WhatsApp and Skype are showing.
This is the URL which send in Skype.
Does anyone know third party library for this feature?
I have also try to get completed HTML page for URL and try to parse XML but in that also I am not able to get all info.
Please help me
What you have to do is read meta tag from any XML Parser, I would recommend using
https://github.com/drmohundro/SWXMLHash
but first you have to read html for the link use SwiftSoup for that
https://github.com/scinfu/SwiftSoup
I am not able to find actual code for it, hence cannot share that here
For this you have to get html string from the particular site like below,
- (void)getHtml
{
NSURLSession *session = NSURLSession.sharedSession;
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:[NSURL URLWithString:#"http://mashable.com/2017/06/27/foursquare-asia-tencent-samsung/?utm_cid=hp-r-1#vanGE2s_tkq0"]
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSString *htmlString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"http string: %#", htmlString);
}];
[dataTask resume];
}
Once you get html, you have to parse html and get your tags which you what to display.
Html parser you can use HTMLKit

Objective-C - method for bool from webservice

I have this web service:
<boolean xmlns="http://schemas.microsoft.com/2003/10/Serialization/">true</boolean>
And I have this method here:
-(void)deviceCheck:(NSString *)device Completetion:(void (^) (NSArray * result,NSError * error))completion{
NSString *deviceRequestString = [NSString stringWithFormat:#"%#?device=%#",webservice,device];
NSURL *JSONURL = [NSURL URLWithString:deviceRequestString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:JSONURL];
NSURLSessionDataTask * dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if(data == nil){
completion(nil,error);
return;
}
NSError *myError;
NSArray *tableArray = [[NSArray alloc]initWithArray:[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&myError]];
completion(tableArray,myError);
}];
[dataTask resume];
}
but when i call this:
[self deviceCheck:[[UIDevice currentDevice] name] Completetion:^(NSArray *result, NSError *error) {
if(result == nil){
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:#"Message" message:#"Device is not valid." preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* ok = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault handler:nil];
[alertController addAction:ok];
[self presentViewController:alertController animated:YES completion:nil];
}else{
}
}];
The result is nil.
What am I doing wrong here? How would I call this web service that is a boolean.
I did an NSLog on error:
Error Domain=NSURLErrorDomain Code=-1022 "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.
Fixed that error with this: https://github.com/meteor/meteor/issues/4560
Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set."
You are using NSJSONSerialization to parse the response of your web service. But your web service is returning XML and not JSON.
So what you want to do is either see if your web service can return JSON responses or change your code to parse that XML response.
You can use the built-in NSXMLParser class to parse the XML response. But it is low level and will require a good amount of code.
The resource could not be loaded because the App Transport Security policy requires the use of a secure connection.
Since iOS9, Apple requires you to talk HTTP to properly configured SSL endpoints. If your web service is not using a proper SSL endpoint then you need to fix that.
If you cannot fix your web service to use a good SSL configuration then you must think hard about wether this is a good idea. If your service is accessible over plain HTTP without SSL then it is trivial for attackers in many scenarios to listen in and even modify calls to your web service.
If you would like to make an exception to the App Transport Security requirements then you can do so in your application's Info.plist file. This is well documented in the App Transport Security Technote
Personal opinion: Disabling ATS completely, by setting NSAllowsArbitraryLoads to YES, is not a solution. It is usually a lazy workaround that possibly puts your users at risk. Specially in 2015 where unwarranted surveillance, identity theft and data breaches happen more often than you wish for.

error back when using nsurlsession to access a webservice several times in a for loop

I need to get image information from server, such image name, image id. Then use image id as one of parameters to make post, get image actual data. More specific, there are three images I should get.
First, I use getImageInfo to get image information.
- (void)getImageInfo {
// compose request
NSUserDefaults *getUserInfo = [NSUserDefaults standardUserDefaults];
NSString *uid = [getUserInfo objectForKey:#"uid"];
NSString *checkCode = [getUserInfo objectForKey:#"checkCode"];
NSString *data = [NSString stringWithFormat:#"uid=%#&yangzhengma=%#", uid, checkCode];
NSURL *url = [NSURL URLWithString:#"http://121.199.35.173:8080/xihuan22dcloud/services/Shibietupianservice/serviceGetallshibietu"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPBody = [data dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = #"POST";
[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
if (!error) {
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 200) {
// parse data in ram and put into images' imageInfos array
[self.images parseImageInfo:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]];
[self getImageRawData];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}
}] resume];}
Then I use getImageRawData to get three image data.
- (void)getImageRawData {
// compose request dynamically
NSUserDefaults *getUserInfo = [NSUserDefaults standardUserDefaults];
NSString *uid = [getUserInfo objectForKey:#"uid"];
NSString *checkCode = [getUserInfo objectForKey:#"checkCode"];
NSURL *url = [NSURL URLWithString:#"http://121.199.35.173:8080/xihuan22dcloud/services/Shibietupianservice/serviceGetthetupian"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = #"POST";
NSInteger count = 0;
for (ImageInformation *temp in self.images.imageInfos) {
NSString *data = [NSString stringWithFormat:#"uid=%#&yangzhengma=%#&tupianid=%#", uid, checkCode, temp.imageId];
request.HTTPBody = [data dataUsingEncoding:NSUTF8StringEncoding];[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
// if client side is no errors, continue
if (!error) {
// if server side is no errors, continue
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 200) {
NSLog(#"图片内容:%#", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
// in ram and put into images' imageRawData array
[self.images parseImageRawData:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] withImageId:temp.imageId withIndex:count];
// store data to disk
// NSString *path = [[NSString alloc] initWithFormat:#"image%#", temp.imageId];
// [FCFileManager writeFileAtPath:path content:data];
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
}
}
}] resume];
count++;
}}
Here, it will loop three times, three responses come back, only the last one is complete, the others carry a error message, or incomplete raw data sometimes. Now I'm diving into concurrency programming guide, I guess serial queue likely can solve this problem.
Output like this:
2014-12-16 22:38:48.739 WeddingNewVersion[997:83366] 图片内容:<ns:serviceGetthetupianResponse xmlns:ns="http://serviceimpl.my.com"><ns:return>error</ns:return></ns:serviceGetthetupianResponse>
2014-12-16 22:38:48.749 WeddingNewVersion[997:83366] 图片内容:<ns:serviceGetthetupianResponse xmlns:ns="http://serviceimpl.my.com"><ns:return>error</ns:return></ns:serviceGetthetupianResponse>
2014-12-16 22:38:51.943 WeddingNewVersion[997:83366] 图片内容:<ns:serviceGetthetupianResponse xmlns:ns="http://serviceimpl.my.com"><ns:return>/9j/...(complete data)...9k=%%226654474.0</ns:return></ns:serviceGetthetupianResponse>
parameters of requests:
2014-12-17 14:59:25.364 WeddingNewVersion[1875:226651] uid=6&yangzhengma=odWoDXWcBv1jOrEhywkq7L&tupianid=41
2014-12-17 14:59:25.368 WeddingNewVersion[1875:226651] uid=6&yangzhengma=odWoDXWcBv1jOrEhywkq7L&tupianid=42
2014-12-17 14:59:25.368 WeddingNewVersion[1875:226651] uid=6&yangzhengma=odWoDXWcBv1jOrEhywkq7L&tupianid=43
the problem is likely not in composing request.
------------------------------------------------update1-----------------------------------------------
I have tried to put data task of session into a serial queue. Disappointed, this is not working.
dispatch_async(self.serialQueue, ^{
[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){...}] resume];
});
Meanwhile, I make delegateQueue of session as nil, reference says if nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.
Now I am still confused how to make it right.
-----------------------------------------------update2------------------------------------------------
I add [NSThread sleepForTimeInterval:0.5] into the block dispatched to serial queue.
dispatch_async(self.serialQueue, ^{
[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){...}] resume];
[NSThread sleepForTimeInterval:0.5];
});
It does not work. The three responses are complete, but they are all the same.
Thank you in advance!
I'm just guessing as I've never tried it, but possibly your data tasks are all using the same TCP port on your end.
That would be OK if they were serialized - one after the other, in sequence - but if they overlap, then the server would receive garbled HTTP requests:
GET /foo
GET /bar
GET /baz
What the server would see might be something like:
GET /fGET /baroo
GET /baz
That your third requests actually works OK might be an accident of the timing.
If you absolutely require the three requests to be issued simultaneously, there are ways to open three different ports on your end. I don't know how to do it with Cocoa and Objective-C, but you can certainly do it with C and Berkeley Socket system calls. The Cocoa / Cocoa Touch networking methods are just wrappers around sockets.
A couple of thoughts:
Your technique of using a single NSMutableURLRequest instance, and repeatedly mutating it for each request (while the prior requests are still in progress) is curious.
In the spirit of thread safety, I would use a separate NSMutableURLRequest for each concurrent request. You don't want to risk having your thread issuing these requests mutate the request object while some background thread performing one of the prior requests. (See Apple's Thread Safety Summary in the Threading Programming Guide in which they point out that mutable classes are not generally thread safe.)
Having said that, the NSURLConnection documentation leaves us with the impression that this request object would be copied, mitigating this problem. I don't see this sort of assurance in the NSURLSession documentation (though I suspect it does the same thing).
I don't think this is the problem here (if this was the problem, the problem would likely be more erratic than what you report, and besides, I suspect that NSURLSession is handling this gracefully, anyway), but as a matter of good thread-safe coding habits, it would be prudent to let each concurrent request have its own NSMutableURLRequest object.
You have confirmed that the information being used in the requests looks valid.
If you wanted to take this to the next level, you might use Charles (or Wire Shark or whatever tool you prefer) to observe the actual requests as they go out. These sorts of tools are invaluable for debugging these sorts of problems.
If you observe the requests in Charles and confirm that they are valid, then this categorically eliminates client-side issues from the situation.
What is curious is that you are not receiving NSError object from dataTaskWithRequest. Nor are you receiving statusCode other than 200 from your server. That means that your requests were successfully sent to the server and received by the server.
Instead, the server is processing the request, but is having a problem fulfilling the request. This leads me to wonder about the server code, itself. I suspect that there is something in the server code that is preventing concurrent operations from taking place (e.g., locking some shared resource, such as temp file or SQL table, for the duration of the request). I would take a hard look at the server code and make sure there are no potential contention issues.
Furthermore, I would modify the server code to not simply report "error", but rather to produce a meaningful error message (e.g. system provided error messages, error codes, etc.). Your server is detecting an error, so you should have it tell you precisely what that error was.
Note, I am explicitly not advising you to make your requests run sequentially. That is inadvisable. While it might solve the immediate problem, you pay a huge performance penalty doing that, and it's not scalable. And remember, you really must handle concurrent requests gracefully, as you're likely to have multiple users of the app at some point.
I would take a hard look at the server code, adding further debugging information to the error messages in order to track down the problem.
I put request into for loop, it works. The first thought of rob about NSMutableRequest and NSURLSession seems right, I'm trying to catch the whole idea. Thanks for rob's answer. Anyway, this is code.
for (ImageInformation *temp in self.images.imageInfos) {
// compose request dynamically
NSURL *url = [NSURL URLWithString:#"http://121.199.35.173:8080/xihuan22dcloud/services/Shibietupianservice/serviceGetthetupian"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = #"POST";
NSString *data = [NSString stringWithFormat:#"uid=%#&yangzhengma=%#&tupianid=%#", uid, checkCode, temp.imageId];
request.HTTPBody = [data dataUsingEncoding:NSUTF8StringEncoding];
// data task
dispatch_async(self.serialQueue, ^{
[[self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
// if client side is no errors, continue
if (!error) {
// if server side is no errors, continue
NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response;
if (httpResp.statusCode == 200) {
// in ram and put into images' imageRawData array
[self.images parseImageRawData:[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] withImageId:temp.imageId];
// store data to disk
// [FCFileManager writeFileAtPath:path content:data];
// dispatch display image task to main
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.images.imageDrawDatasDic count] == [self.images.imageInfos count]) {
[self.tableView reloadData];
}
});
}
}
}] resume];
[NSThread sleepForTimeInterval:0.5];
});
}
}

NSURLConnection Fails with error codes -1001,-1005,1004 and also NSPOSIXErrorDomain Code=2

We have more number of devices(200+) connected to network to communicate with server, often the connection fails and does not reconnect as mentioned in the question I am getting the mentioned errors.
The code which we use to send request is
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://ipaddress"];
NSMutableURLRequest *menuRequest=[[NSMutableURLRequest alloc]initWithURL:url];
NSURLResponse *response;
NSError *error=nil;
NSData *data = [NSURLConnection sendSynchronousRequest:menuRequest returningResponse:&response error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
if(!error)
{
NSLog(#"Request Success");
NSXMLParser *xmlParser = [[NSXMLParser alloc] initWithData: data];
[xmlParser setDelegate:self];
[xmlParser parse];
}
else
{
NSLog(#"Failed with error-----%#",error);
}
});
});
This request is sent every minute.
It happens only in client environment we need to recover from this connection failure and reconnect(the next request does not get success even if the server is up).This happens in both iOS 7 & 8.I went through some similar post but I am not able to get the exact reason and also the solution for these problems.Help me out guys.
"I went through some similar post but i am not able to get the exact reason and alos the solution for these problems.Help me out guys."
The errors you are getting are the reasons. Specifically, codes -1001, -1005, and -1004 in the NSURLErrorDomain are time outs, network connection lost, and cannot connect to host. You can look these error codes up in the NSURLErrors.h header. These are all problems with your network connectivity.
The code you have posted has no logic to reconnect, which is why it does not attempt to do so.
Like #quellish said, these errors tell you what happened. I googled a thousand times to investigate codes like -1004. But it is simpler to find them locally...
XCode / Window / Developer Documentation / URL Loading System Error Codes

NSURLSession Not Reaching Server

I've created an iPhone app for my Arduino, and basically, the Arduino can communicate over the local network using very basic commands provided by a 3rd party REST API. I've successfully been able to use the API via my computer's web browser, but when trying to send a request to it via an iPhone app, it doesn't seem to want to work. Also keep in mind, I can get the API to respond properly via Safari on my iPhone. The only response I'm getting (inside the console) is:
{ URL: http://192.168.0.216/mode/7/0 } { status code: 200, headers {
Connection = close;
"Content-Type" = "application/json";
} } : <7b226d65 73736167 65223a20 2250696e 20443722 6964223a 20223030 38222c20 226e616d 65223a20 226d6967 6874795f 63617422 2c202263 6f6e6e65 63746564 223a2074 7275657d 0d0a>
The API is indeed supposed to return JSON data, but the response on the web browser actually affects my Arduino's LED.
Code for Turning the LED on
NSURL *modeSet = [NSURL URLWithString:[NSString stringWithFormat:#"http://192.168.0.216/digital/%d/1", _pin]];
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:modeSet
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
NSLog([NSString stringWithFormat:#"%# : %#", response, data]);
}] resume];
EDIT: I decided to print out the 'Error' variable to see if it was holding anything back from me, and I found this:
Error Domain=NSURLErrorDomain Code=-1001 "The operation couldn’t be completed.
(NSURLErrorDomain error -1001.)" UserInfo=0x17807b840 {NSErrorFailingURLStringKey=http://192.168.0.216/mode/7/o,
NSUnderlyingError=0x178449450 "The operation couldn’t be completed.
(kCFErrorDomainCFNetwork error -1001.)", NSErrorFailingURLKey=http://192.168.0.216/mode/7/o}
Pre-iOS 9 Answer
Answering my own question so if anyone finds this by Google sometime, they won't have to ask.
All I did was formatted my string correctly with NSUTF8Encoding like so:
NSString *modeSetString = [[NSString stringWithFormat:#"http://192.168.0.216/mode/%d/o", _pin] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *modeSet = [NSURL URLWithString:modeSetString];
iOS 9 Update
stringByReplacingPercentEscapesUsingEncoding: is now deprecated and stringByRemovingPercentEncoding should be used instead like so:
NSString *modeSetString = [[NSString stringWithFormat:#"http://192.168.0.216/mode/%d/o", _pin] stringByRemovingPercentEncoding];
NSURL *modeSet = [NSURL URLWithString:modeSetString];

Resources