checking reachability against a specific page in a URL - ios

After I have read the answer for this question I have found that using reachabilityWithHostName does not work with a URL such as this one: mySite.com/service.asmx , is there anyway to check reachability against this URL using reachabilityWithHostName or any reachability class method ?
thanks so much in advance.

The Reachability class and -reachabilityWithHostname: is designed to be a quick, fail-fast mechanism to determine whether you have basic network connectivity to the host. If you need to verify that a particular URL can be downloaded, you need to be looking at using NSURLConnection to retrieve the contents of the URL in order to verify that it is truly available.
Depending on whether you need to do this in the foreground or background, you can either use the simple-but-blocking:
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
or you can use the more complicated method of creating an NSURLConnection object, setting up a delegate to receive responses and wait for those responses to come in.
For the simple case:
NSURL *myURL = [NSURL URLWithString: #"http://example.com/service.asmx"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: myURL];
[request setHTTPMethod: #"HEAD"];
NSURLResponse *response;
NSError *error;
NSData *myData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];
If you receive back a non-nil myData, you've got some kind of connectivity. response and error will tell you what the server responded to you (in the case of response and if you received a non-nil myData) or what kind of error occurred, in the case of a nil myData.
For the non-trivial case, you can get good guidance from Apple's Using NSURLConnection.
If you don't want to stall your foreground process, you can do this two different ways. The above documentation will provide information on how to implement the delegate, etc. However, a simpler implementation would be to use GCD to send the Synchronous request on a background thread, and then message yourself on the main thread when you are done.
Something like this:
NSURL *myURL = [NSURL URLWithString: #"http://example.com/service.asmx"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: myURL];
[request setHTTPMethod: #"HEAD"];
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, NULL), ^{
NSURLResponse *response;
NSError *error;
NSData *myData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];
BOOL reachable;
if (myData) {
// we are probably reachable, check the response
reachable=YES;
} else {
// we are probably not reachable, check the error:
reachable=NO;
}
// now call ourselves back on the main thread
dispatch_async( dispatch_get_main_queue(), ^{
[self setReachability: reachable];
});
});

If you want to check reachability against a URL (the one usually used is against a hostname) just do a HEAD request using a NSURLConnection.

Swift 5
A possible solution for Swift is:
func verifyURL(urlPath: String, completion: #escaping (_ isValid: Bool) ->()) {
if let url = URL(string: urlPath) {
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
let task = URLSession.shared.dataTask(with: request) { _, response, error in
if let httpResponse = response {
if httpResponse.getStatusCode() == 200 {
completion(true)
}
} else {
completion(false)
}
}
task.resume()
} else {
completion(false)
}
}
Then call the method like that:
verifyURL(urlPath: "www.google.com", completion: { (isValid) in
if isValid {
runYourCode()
} else {
print("URL: www.google.com is not reachable")
}
})

Related

Allow async function to only be called once (make others wait)

I have an authorisation function that will run in my app if it makes an unauthorised request. I originally built the auth function in a POC and now I have transferred it into the app I am working on. I've noticed an interesting issue where if I set the token to invalid on launch (and this only happens on launch) then about 4 auth requests are sent out from the app. From what I can tell, the response for the auth returns after the requests are sent out so a few stack up before the app realises it has a valid token. The function works perfectly if an auth token expires whilst using the app, this issue is purely on load.
I've decided the solution to this problem is to make the auth function exclusive. I did a little research into allowing a function to be called once and came across #synchronized. It made sense but I'm not too sure how I can implement it into my function. My auth function looks like this:
+ (void)requestAuthTokenWithBlock:(void (^)(void))completion {
NSLog(#"requestNewToken - Called: Requesting a new authorization bearer token.");
[[NSURLCache sharedURLCache] removeAllCachedResponses];
//Build request URL String
NSString *requestString = [NSString stringWithFormat:#"%#%#",baseURL,authRequestURL];
//Encode password so that it can be safely sent in request
NSString *encodedPassword = [kU1Password stringByAddingPercentEncodingForRFC3986];
//Populate post request with user credentials
NSString *post = [NSString stringWithFormat:#"client_id=%#&password=%#&grant_type=%#", kU1ClientId, encodedPassword, kU1GrantType];
//Encode post string & convert to type NSData
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
//Calculate the length of the post string
NSString *postLength = [NSString stringWithFormat:#"%lu",(unsigned long)[postData length]];
//Initialize url request
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
//Set the url for which you will pass your request data
[request setURL:[NSURL URLWithString:requestString]];
//Set HTTP method for request
[request setHTTPMethod:#"POST"];
//Set HTTP header field with length of post data
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
//Set the encoded value for HTTP Header field
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
//Set the HTTP body of the urlrequest with our post data
[request setHTTPBody:postData];
//Create full request
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error){
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(#"Status Code: %ld\n - requestAuthTokenWithBlock - ",(long)httpResponse.statusCode);
NSString *message = [NSHTTPURLResponse localizedStringForStatusCode:httpResponse.statusCode];
NSLog(#"Message: %#", message);
//Check for an error, if there is no error we proceed.
if (!error) {
NSLog(#"requestAuthToken - Successful responce from server");
//Populate the auth object with the parse json data (handled entirely in the builder)
AuthToken *auth = [TokenBuilder authFromJSON:data error:&error];
//Save the auth & refresh tokens in the keychain
[SAMKeychain setPassword:auth.AuthToken forService:kServer account:kKeyAccessToken];
[SAMKeychain setPassword:auth.RefreshToken forService:kServer account:kKeyRefreshToken];
//Trigger completion block
if (completion)
{
completion();
}
}
else {
//Failed request
//Trigger completion block
if (completion)
{
completion();
}
}
}];
[dataTask resume];
}
And it only ever gets called from this switch statement if the app has a token:
switch (httpResponse.statusCode) {
case 200 ... 299:{
NSLog(#"SUCCESS");
NSLog(#"Performing any completion related functions!");
retryAttempts = 0;
completion(true, message, data, nil);
return;
}
case 401:{
if (retryAttempts == 0){
NSLog(#"401 Challenge - Retrying Authentication, First Attempt:");
}
else {
NSLog(#"401 Challenge - Retrying Authentication, Attempt %ld", (long)retryAttempts);
}
[self requestAuthTokenWithBlock:^(void){
NSLog(#"requestAuthTokenWithBlock - Completion block called");
[self dataTaskwithRequest:request method:method completion:completion];
}];
retryAttempts += 1;
break;
}
}
I copied out the #Synchronized snippet but didn't really know where to put it:
#synchronized (<#token#>) {
<#statements#>
}
I tried putting it into the token method where I call the datatask:
#synchronized (dataTask) {
[dataTask resume];
}
and the function still got called more than once. I'm not sure how to tackle this issue, I can add a call to this function from anywhere in the app and don't think I can control the requests themselves. I think the only place to protect the function from multiple calls is in the function itself. And as a sidenote I added a boolean value that gets set to false when the function starts and true when the function ends.
if (canAttempt == NO) {
return;
}
canAttempt = NO;
And now the functions that once got called 4 times now return as failed because they weren't authorised. Any ideas on how I can get the app to wait on this function before continuing?

NSURLConnection send request after finish all process

I have a nested loop of sending the request.
-(void) download
{
for(NSString *id in array)
{
//init with request and start the connection
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy: NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request deletegate:self];
[conn start];
}
}
-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *) data
{
//enter here secondly
}
-(void) connectionDidFinishLoading:(NSURLConnection *) connection
{
//enter here last, after finish the for loop
//my intention is use the downloaded data to do something before sending a new request.
}
The problem is that I want to enter "-(void) connectionDidFinishLoading:(NSURLConnection *) connection" first before send the request again in the for loop.
But currently it will finish the for loop and sent all the request before enter to "-(void) connectionDidFinishLoading:(NSURLConnection *) connection".
You Should Try This NSURLConnection is deprecated in iOS9
for (NSString *URL in URLArray) {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
NSURLSessionTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// check error and/or handle response here
}];
[task resume];
}
and use dispatch_group_t group = dispatch_group_create();
add line to for loop dispatch_group_enter(group); will call
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// Request Finish
});
for your goal
In your case you need to try block function because as per your requirement you want response of the first connection for another request.
for(NSString* url in array)
{
// Generate a NSURLRequest object from the address of the API.
NSURL *url = [NSURL URLWithString:urlLink];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// Send the request asynchronous request using block!
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
NSLog(#"Error in updateInfoFromServer: %# %#", error, [error localizedDescription]);
} else if (!response) {
NSLog(#"Could not reach server!");
} else if (!data) {
NSLog(#"Server did not return any data!");
} else {
[self doStuffWithData:data];
}
}];
}
URL loading is not a synchronous operation (or at least should never be done synchronously), because it can take up to 90 seconds just for a DNS lookup failure, and almost infinitely long if the server keeps dribbling out data. If you block the main thread for even a fraction of that amount of time, iOS will kill your app.
Instead of scheduling the requests in a loop and waiting for them to finish, you need to schedule the first request (and only the first request). Then, in your connectionDidFinishLoading: method (and maybe your connection:DidFailWithError: method), schedule the next request.
With that said, unless you still need to support iOS 6/10.8 and earlier, you should probably be using NSURLSession. (The same general advice applies; the delegate method names are changed to protect the guilty.)

returning a value from asynchronous call using semaphores

I need to use NSURLSession to make network calls. On the basis of certain things, after I receive the response, I need to return an NSError object.
I am using semaphores to make the asynchronous call behave synchronously.
The problem is, the err is set properly inside call, but as soon as semaphore ends (after
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
), the err becomes nil.
Please help
Code:
-(NSError*)loginWithEmail:(NSString*)email Password:(NSString*)password
{
NSError __block *err = NULL;
// preparing the URL of login
NSURL *Url = [NSURL URLWithString:urlString];
NSData *PostData = [Post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
// preparing the request object
NSMutableURLRequest *Request = [[NSMutableURLRequest alloc] init];
[Request setURL:Url];
[Request setHTTPMethod:#"POST"];
[Request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[Request setHTTPBody:PostData];
NSMutableDictionary __block *parsedData = NULL; // holds the data after it is parsed
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.TLSMinimumSupportedProtocol = kTLSProtocol11;
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
NSURLSessionDataTask *task = [session dataTaskWithRequest:Request completionHandler:^(NSData *data, NSURLResponse *response1, NSError *err){
if(!data)
{
err = [NSError errorWithDomain:#"Connection Timeout" code:200 userInfo:nil];
}
else
{
NSString *formattedData = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", formattedData);
if([formattedData rangeOfString:#"<!DOCTYPE"].location != NSNotFound || [formattedData rangeOfString:#"<html"].location != NSNotFound)
{
loginSuccessful = NO;
//*errorr = [NSError errorWithDomain:#"Server Issue" code:201 userInfo:nil];
err = [NSError errorWithDomain:#"Server Issue" code:201 userInfo:nil];
}
else
{
parsedData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&err];
NSMutableDictionary *dict = [parsedData objectForKey:#"User"];
loginSuccessful = YES;
}
dispatch_semaphore_signal(semaphore);
}];
[task resume];
// but have the thread wait until the task is done
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return err;
}
Rob's answer tells you how to do it right, but not what mistake you made:
You have two variables named err, which are totally unrelated. It seems that you haven't turned on some important warnings, otherwise your code wouldn't even have compiled.
The parameter err that is passed to your completion block is the error from the URL request. You replace it without thinking with a timeout error - so the true error is now lost. Consider that timeout is not the only error.
But all the errors that you set only set the local variable err which was passed to you in the completion block; they never touch the variable err in the caller at all.
PS. Several serious errors in your JSON handling. JSON can come in UTF-16 or UTF-32, in which case formattedData will be nil and you incorrectly print "Server Issue". If the data isn't JSON there is no guarantee that it contains DOCTYPE or html, that test is absolute rubbish. Your user with the nickname JoeSmith will hate you.
Passing NSJSONReadingAllowFragments to NSJSONSerialization is nonsense. dict is not mutable; if you try to modify it your app will crash. You don't check that the parser returned a dictionary, you don't check that there is a value for the key "User", and you don't check that the value is a dictionary. That's lots of ways how your app can crash.
I would suggest cutting the Gordian knot: You should not use semaphores to make an asynchronous method behave synchronously. Adopt asynchronous patterns, e.g. use a completion handler:
- (void)loginWithEmail:(NSString *)email password:(NSString*)password completionHandler:(void (^ __nonnull)(NSDictionary *userDictionary, NSError *error))completionHandler
{
NSString *post = ...; // build your `post` here, making sure to percent-escape userid and password if this is x-www-form-urlencoded request
NSURL *url = [NSURL URLWithString:urlString];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
// [request setValue:postLength forHTTPHeaderField:#"Content-Length"]; // not needed to set length ... this is done for you
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"]; // but it is best practice to set the `Content-Type`; use whatever `Content-Type` appropriate for your request
[request setValue:#"text/json" forHTTPHeaderField:#"Accept"]; // and it's also best practice to also inform server of what sort of response you'll accept
[request setHTTPBody:postData];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.TLSMinimumSupportedProtocol = kTLSProtocol11;
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:nil delegateQueue:nil];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *err) {
if (!data) {
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(nil, [NSError errorWithDomain:#"Connection Timeout" code:200 userInfo:nil]);
});
} else {
NSError *parseError;
NSDictionary *parsedData = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&parseError];
dispatch_async(dispatch_get_main_queue(), ^{
if (parsedData) {
NSDictionary *dict = parsedData[#"User"];
completionHandler(dict, nil);
} else {
completionHandler(nil, [NSError errorWithDomain:#"Server Issue" code:201 userInfo:nil]);
}
});
}
}];
[task resume];
}
And then call it like so:
[self loginWithEmail:userid password:password completionHandler:^(NSDictionary *userDictionary, NSError *error) {
if (error) {
// do whatever you want on error here
} else {
// successful, use `userDictionary` here
}
}];
// but don't do anything reliant on successful login here; put it inside the block above
Note:
I know you're going to object to restoring this back to asynchronous method, but it's a really bad idea to make this synchronous. First it's a horrible UX (the app will freeze and the user won't know if it's really doing something or whether it's dead) and if you're on a slow network you can have all sorts of problems (e.g. the watchdog process can kill your app if you do this at the wrong time).
So, keep this asynchronous. Ideally, show UIActivityIndicatorView before starting asynchronous login, and turn it off in the completionHandler. The completionHandler would also initiate the next step in the process (e.g. performSegueWithIdentifier).
I don't bother testing for HTML content; it is easier to just attempt parse JSON and see if it succeeds or not. You'll also capture a broader array of errors this way.
Personally, I wouldn't return my own error objects. I'd just go ahead and return the error objects the OS gave to me. That way, if the caller had to differentiate between different error codes (e.g. no connection vs server error), you could.
And if you use your own error codes, I'd suggest not varying the domain. The domain should cover a whole category of errors (e.g. perhaps one custom domain for all of your app's own internal errors), not vary from one error to another. It's not good practice to use the domain field for something like error messages. If you want something more descriptive in your NSError object, put the text of the error message inside the userInfo dictionary.
I might suggest method/variable names to conform to Cocoa naming conventions (e.g. classes start with uppercase letter, variables and method names and parameters start with lowercase letter).
There's no need to set Content-Length (that's done for you), but it is good practice to set Content-Type and Accept (though not necessary).
You need to let the compiler know that you will be modifying err. It needs some special handling to preserve that beyond the life of the block. Declare it with __block:
__block NSError *err = NULL;
See Blocks and Variables in Blocks Programming Topics for more details.

Host timing out in Terminal and showing as connected in reachability iOS

Today, I was testing the reachabilityWithHostName, and I'm typing a domain that is timing out inside the network that I'm joined, but the reachability its returning as its there a connection.
Why is might this be occurring?
Thanks
Here is my ping log:
PING 188.121.62.144 (188.121.62.144): 56 data bytes
Request timeout for icmp_seq 0
Request timeout for icmp_seq 1
Request timeout for icmp_seq 2
Request timeout for icmp_seq 3
Request timeout for icmp_seq 4
Request timeout for icmp_seq 5
Request timeout for icmp_seq 6
Here is my code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
Reachability* reachability = [Reachability reachabilityWithHostName:#"188.121.62.144"];
NetworkStatus remoteHostStatus = [reachability currentReachabilityStatus];
if(remoteHostStatus == NotReachable)
{
isInternet = NO;
}
else if (remoteHostStatus == ReachableViaWWAN)
{
isInternet = TRUE;
}
else if (remoteHostStatus == ReachableViaWiFi)
{
isInternet = TRUE;
}
if (!isInternet)
{
NSLog(#"No connection");
}
else
{
NSLog(#"There is connection");
}
return YES;
}
Can you try to ping with the below modified code .
Reachability* reachability = [Reachability reachabilityWithHostName:#"http://188.121.62.144"];
The Reachability class and -reachabilityWithHostname: is designed to be a quick, fail-fast mechanism to determine whether you have basic network connectivity to the host. If you need to verify that a particular URL can be downloaded, you need to be looking at using NSURLConnection to retrieve the contents of the URL in order to verify that it is truly available.
Depending on whether you need to do this in the foreground or background, you can either use the simple-but-blocking:
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
or you can use the more complicated method of creating an NSURLConnection object, setting up a delegate to receive responses and wait for those responses to come in.
For the simple case:
NSURL *myURL = [NSURL urlWithString: #"http://188.121.62.144"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: myURL];
[request setHTTPMethod: #"HEAD"];
NSURLResponse *response;
NSError *error;
NSData *myData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];
If you receive back a non-nil myData, you've got some kind of connectivity. response and error will tell you what the server responded to you (in the case of response and if you received a non-nil myData) or what kind of error occurred, in the case of a nil myData.
For the non-trivial case, you can get good guidance from Apple's Using NSURLConnection.
If you don't want to stall your foreground process, you can do this two different ways. The above documentation will provide information on how to implement the delegate, etc. However, a simpler implementation would be to use GCD to send the Synchronous request on a background thread, and then message yourself on the main thread when you are done.
Something like this:
NSURL *myURL = [NSURL urlWithString: #"http://188.121.62.144"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: myURL];
[request setHTTPMethod: #"HEAD"];
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, NULL), ^{
NSURLResponse *response;
NSError *error;
NSData *myData = [NSURLConnection sendSynchronousRequest: request returningResponse: &response error: &error];
BOOL reachable;
if (myData) {
// we are probably reachable, check the response
reachable=YES;
} else {
// we are probably not reachable, check the error:
reachable=NO;
}
// now call ourselves back on the main thread
dispatch_async( dispatch_get_main_queue(), ^{
[self setReachability: reachable];
});
});

Creating a Post Request in iOS

To begin with, I have looked up how to make a post request and have read multiple thread and docs about how to create one, however my data doesn't seem to be working.
I have two fields say x and html that i want to make to the call callname.
The GET form of this would be www.someserver.com/callname?x=something&y=something
Here is what my POST code looks like so far:
NSString *baseURLString = #"http://www.someserver.com/callname"
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[[NSURL URLWithString:baseURLString] standardizedURL]];
NSString *fields = [NSString stringWIthFormat:#"x=%#&html=%#",x,htmlSource];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/x-www-form-urlencoded; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:[NSData dataWithBytes:[fields UTF8String] length:strlen([fields UTF8String])]];
NSURLConnection *connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
The NSURLConnection Delegate Methods implemented as such
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
NSLog(#"Data Received");
}
-(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
NSLog(#"Error: %#" , error);
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSLog(#"Connection Finished");
}
Did i miss something? My code looks pretty identical to most of the examples I found aside from the choice of encoding. Does it matter that I am passing in html source code as the value of y? Any tips or hints will be appreciated. I am very new to iOS and html handling in general so excuse my lack of knowledge on the subject. Thanks for your time!
You must implement the delegate protocol for NSURLConnection to get the response. You haven't posted any of that code, so I would assume this means you didn't implement the relevant methods.
One potential issue is that you missed to properly percent encode the parameters:
When using parameters like in your code with a content type application/x-www-form-urlencoded, I would suggest to create your parameters as (unencoded) NSString key/value pairs, create a NSDictionary object and use the following helper method described in the answer here (How to send multiple parameterts to PHP server in HTTP post) which creates a properly encoded parameter string which you can add to the body.
I believe your form URL encoded is not correct. I believe the solution is the following
[serviceRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
If you want to UTF-8 encode your values, you need to do it before, like for example,
NSString *postLength = [NSString stringWithFormat:#"%d", [_xmlDoc length]]; //Calculating the Content Length
NSData *postData = [_xmlDoc dataUsingEncoding:NSUTF8StringEncoding]; // preparing XML to be sent in POST
Notice you encode the string of POST data ahead of time.
Hope that this helps!!
class Requests {
class func loginRequest(userName:String, password:String, completion: #escaping ((JSON?, String?) -> ()) )
{
var request = URLRequest(url: URL(string: "your URL")!)
request.httpMethod = "POST"
//parameters
let postString = "user_login=\(userName)&user_pass=\(password)"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) {
data, response, error in guard error == nil else {
print(error!)
completion(nil, error!.localizedDescription)
return
}
guard let data = data else {
completion(nil, "location not found")
return
}
let jsonData = JSON(data)
completion(jsonData, nil)
}
task.resume()
}
}
Your code works for me. Did you remember to run [connection start]; ?

Resources