Simpliest solution to check if File exists on a webserver. (Swift) - swift

There are a lot of discussion about this and I understand the solution to use the delegate method and check the response "404":
var request : NSURLRequest = NSURLRequest(URL: url)
var connection : NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)!
connection.start()
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
//...
}
But I would like to have a simple solution like:
var exists:Bool=fileexists(sURL);
Because I will have a lot of request in the same class with the delegate and I only want to check the response with my function fileexists().
Any hints ?
UPDATE
I guess I'll have to do a synchronious request like the following, but I get always 0x0000000000000000 as a response::
let urlPath: String = sURL;
var url: NSURL = NSURL(string: urlPath)!
var request1: NSURLRequest = NSURLRequest(URL: url)
var response: AutoreleasingUnsafeMutablePointer<NSURLResponse?
>=nil
var error: NSErrorPointer = nil
var dataVal: NSData = NSURLConnection.sendSynchronousRequest(request1, returningResponse: response, error:nil)!
var err: NSError
println(response)

Swift 3.0 version of Martin R's answer written asynchronously (the main thread isn't blocked):
func fileExistsAt(url : URL, completion: #escaping (Bool) -> Void) {
let checkSession = Foundation.URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
request.timeoutInterval = 1.0 // Adjust to your needs
let task = checkSession.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if let httpResp: HTTPURLResponse = response as? HTTPURLResponse {
completion(httpResp.statusCode == 200)
}
})
task.resume()
}

Checking if a resource exists on a server requires sending a HTTP
request and receiving the response. TCP communication can take some
amount of time, e.g. if the server is busy, some router between the
client and the server does not work
correctly, the network is down etc.
That's why asynchronous requests are always preferred. Even if you think
that the request should take only milliseconds, it might sometimes be
seconds due to some network problems. And – as we all know – blocking
the main thread for some seconds is a big no-no.
All that being said, here is a possible implementation for a
fileExists() method. You should not use it on the main thread,
you have been warned!
The HTTP request method is set to "HEAD", so that the server sends
only the response header, but no data.
func fileExists(url : NSURL!) -> Bool {
let req = NSMutableURLRequest(URL: url)
req.HTTPMethod = "HEAD"
req.timeoutInterval = 1.0 // Adjust to your needs
var response : NSURLResponse?
NSURLConnection.sendSynchronousRequest(req, returningResponse: &response, error: nil)
return ((response as? NSHTTPURLResponse)?.statusCode ?? -1) == 200
}

Improved Vito's solution so the completion is always called:
func fileExists(at url: URL, completion: #escaping (Bool) -> Void) {
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
request.timeoutInterval = 1.0 // Adjust to your needs
URLSession.shared.dataTask(with: request) { _, response, _ in
completion((response as? HTTPURLResponse)?.statusCode == 200)
}.resume()
}
// Usage
fileExists(at: url) { exists in
if exists {
// do something
}
}
async/await
func fileExists(at url: URL) async throws -> Bool {
var request = URLRequest(url: url)
request.httpMethod = "HEAD"
request.timeoutInterval = 1.0 // Adjust to your needs
let (_, response) = try await URLSession.shared.data(for: request)
return (response as? HTTPURLResponse)?.statusCode == 200
}
// Usage
if try await fileExists(at: url) {
// do something
}
// or if you don't want to deal with the `throw`
if (try? await fileExists(at: url)) ?? false {
// do something
}

Related

URLSession dataTask method returns 0 bytes of data

I am using URLSession's dataTask method with a completion handler. The error in response is nil, but the data object returns something, it returns 0 bytes of data.
I was using Alamofire library firstly, I thought there is something wrong with it because I started using newer version so I stated using my own implementation of Alamofire just so I don't have to rewrite all the calls I already have in my app.
It still returns 0 bytes of data.
When I use the same URL in the Playground with a simple URLSession call, it works and returns the data, do you have any idea what might go wrong?
My implementation of Alamofire (Srsly quick and dirty within 30 minutes):
import Foundation
typealias DataResponse = (data: Data?, response: URLResponse?, error: Error?, request: URLRequest?, result: Result)
public class Alamofire: NSObject {
enum Method: String {
case get = "GET"
case post = "POST"
}
fileprivate static let session = URLSession(configuration: URLSessionConfiguration.default)
public struct request {
var request: URLRequest? = nil
var url: URL? = nil
let method: Alamofire.Method?
let parameters: Parameters?
let headers: Headers?
init(_ request: URLRequest, method: Alamofire.Method? = nil, parameters: Parameters? = nil, headers: Headers? = nil) {
self.request = request
self.url = request.url
self.method = method
self.parameters = parameters
self.headers = headers
}
init(_ url: URL, method: Alamofire.Method? = nil, parameters: Parameters? = nil, headers: Headers? = nil) {
self.url = url
self.request = URLRequest(url: url)
self.method = method
self.parameters = parameters
self.headers = headers
}
}
}
typealias Parameters = [String: Any?]
typealias Headers = [String: String]
extension Alamofire.request {
func responseData(completionHandler: #escaping (_ dataResponse: DataResponse) -> Void) {
guard let request = request else { return }
Alamofire.session.dataTask(with: request) { (data, response, error) in
let result: Result
if let error = error {
result = Result.failure(error)
completionHandler((data, response, error, request, result))
return
}
completionHandler((data, response, error, request, Result.success))
}.resume()
}
}
enum Result {
case success
case failure(Error)
}
So the resolution to my problem was a realisation that the data provider cut me off of data :) Can happen too. That's probably not a "solution" but an option you have to consider as I will from now on :D Thank you all
Did you create an App Transport Security Execption for Allows Arbitrary loads in the info.plist?

How to convert this to a POST call with a JSON serialized Object

I have tried Alamofire, I have tried it with all my heart. It just does not work for me. I finally got http GET working, but I need to get http POST working. Our POST API's take a Request object that has all the necessary data in it. I have requested the backend developers to provide me with a KEY-VALUE pair JSON (no embedded objects/arrays) so that I can use a Dictionary in my swift code convert that to json and send the request. All I need is now to convert the below code to POST.
My earlier questions that did not help much.
NSInvalidArgumentException Invalid type in JSON write DemographicsPojo
Swift 3.0, Alamofire 4.0 Extra argument 'method' in call
I have given up on Alamofire. I want to use Foundation classes. Simple basic and fundamental way of life :).
func callWebService(url : String) {
// Show MBProgressHUD Here
var config :URLSessionConfiguration!
var urlSession :URLSession!
config = URLSessionConfiguration.default
urlSession = URLSession(configuration: config)
// MARK:- HeaderField
let HTTPHeaderField_ContentType = "Content-Type"
// MARK:- ContentType
let ContentType_ApplicationJson = "application/json"
//MARK: HTTPMethod
let HTTPMethod_Get = "GET"
let callURL = URL.init(string: url)
var request = URLRequest.init(url: callURL!)
request.timeoutInterval = 60.0 // TimeoutInterval in Second
request.cachePolicy = URLRequest.CachePolicy.reloadIgnoringLocalCacheData
request.addValue(ContentType_ApplicationJson, forHTTPHeaderField: HTTPHeaderField_ContentType)
request.httpMethod = HTTPMethod_Get
let dataTask = urlSession.dataTask(with: request) { (data,response,error) in
if error != nil{
print("Error **")
return
}
do {
let resultJson = try JSONSerialization.jsonObject(with: data!, options: []) as? [String:AnyObject]
print("Result",resultJson!)
} catch {
print("Error -> \(error)")
}
}
dataTask.resume()
print("..In Background..")
}
Just pass JSON string and the API URL to this function. Complete code for POST.
func POST(api:String,jsonString:String,completionHandler:#escaping (_ success:Bool,_ response:String?,_ httpResponseStatusCode:Int?) -> Void) {
let url = URL(string: api)
var request: URLRequest = URLRequest(url: url!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField:"Content-Type")
request.timeoutInterval = 60.0
//additional headers
if let token = Helper.readAccessToken() {
request.setValue("\(token)", forHTTPHeaderField: "Authorization")
}
let jsonData = jsonString.data(using: String.Encoding.utf8, allowLossyConversion: true)
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) {
(data: Data?, response: URLResponse?, error: Error?) -> Void in
var responseCode = 0
if let httpResponse = response as? HTTPURLResponse {
responseCode = httpResponse.statusCode
print("responseCode \(httpResponse.statusCode)")
}
if error != nil {
completionHandler(false, error?.localizedDescription,nil)
} else {
let responseString = String(data: data!, encoding: .utf8)
completionHandler(true, responseString, responseCode)
}
}
task.resume()
}

Fixing NSURLConnection Deprecation from Swift 1.2 to 2.0

I have a function written in Swift 1.2, that checks for Reachability of Address or IP. Here it is :
func isHostConnected(hostAddress : String) -> Bool
{
var response : NSURLResponse?
let request = NSMutableURLRequest(URL: NSURL(string: hostAddress)!)
request.timeoutInterval = 3
let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: nil)
return ((response as? NSHTTPURLResponse)!.statusCode == 200)
}
Now since, NSURLConnection is deprecated, as per Xcode suggestion I tried writing it using NSURLSession.dataTaskWithRequest, here it is :
func isHostConnected(hostAddress : String) -> Bool
{
let request = NSMutableURLRequest(URL: NSURL(string: hostAddress.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!)!)
request.timeoutInterval = 3
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: nil, delegateQueue: NSOperationQueue.mainQueue())
var responseCode = -1
session.dataTaskWithRequest(request, completionHandler: {(data : NSData?, response : NSURLResponse?, error : NSError?) -> Void in
responseCode = (response as? NSHTTPURLResponse)!.statusCode
})!.resume()
return (responseCode == 200)
}
Above code always returns false (its Obvious), since completionHandler gets executed on seperate Thread.
My concern is that, can I make completionHandler() run on MainThread somehow like sendSynchronousRequest does by blocking it.
I have reasons to not to use 'Apple's Reachabilty' here.
Any suggestion will be helpful. :)
(Repeating my arguments from https://stackoverflow.com/a/30670518/1187415:)
Checking if a resource exists on a server requires sending a HTTP
request and receiving the response. TCP communication can take some
amount of time, e.g. if the server is busy, some router between the
client and the server does not work correctly, the network is down
etc.
That's why asynchronous requests are always preferred. Even if you
think that the request should take only milliseconds, it might
sometimes be seconds due to some network problems. And – as we all
know – blocking the main thread for some seconds is a big no-no.
That being said, you can use a "counting semaphore" or a "dispatch group" to wait for the completion of some asynchronous task.
You should not use this on the main thread. Blocking the main thread
for up to 3 seconds is not acceptable!
func isHostConnected(hostAddress : String) -> Bool
{
let request = NSMutableURLRequest(URL: NSURL(string: hostAddress.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!)!)
request.timeoutInterval = 3
request.HTTPMethod = "HEAD"
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
var responseCode = -1
let group = dispatch_group_create()
dispatch_group_enter(group)
session.dataTaskWithRequest(request, completionHandler: {(_, response, _) in
if let httpResponse = response as? NSHTTPURLResponse {
responseCode = httpResponse.statusCode
}
dispatch_group_leave(group)
})!.resume()
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
return (responseCode == 200)
}
Remarks:
Setting the HTTP method to "HEAD" is a small optimization, as the
server sends only the response header without the actual data.
In the case of a illegal host name, response would be nil, and
responseCode = (response as? NSHTTPURLResponse)!.statusCode would crash.

Prevent redirect response with Alamofire in Swift

I'm looking for example code how to prevent redirect response (status code 3xx) when request web api. I'm using Swift with Alamofire 1.2.
I have tried:
delegate.taskWillPerformHTTPRedirection = { (session: NSURLSession!, task: NSURLSessionTask!, response: NSHTTPURLResponse!, request: NSURLRequest!) in
return nil
}
but not work
I've also tried: https://github.com/Alamofire/Alamofire/pull/350/files and have changed my own code to:
var acc = self.txtAccount.text
var pwd = self.txtPassword.text
var url : String = "http://10.1.0.2:8081/wordpress/wp-json/users/me"
let delegate = Alamofire.Manager.sharedInstance.delegate
delegate.taskWillPerformHTTPRedirection = { (session: NSURLSession!, task: NSURLSessionTask!, response: NSHTTPURLResponse!, request: NSURLRequest!) in
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
request.HTTPMethod = "GET"
var credential = "\(acc):\(pwd)"
var authData = credential.dataUsingEncoding(NSUTF8StringEncoding)
var encodedAuthData = authData?.base64EncodedStringWithOptions(nil)
var authValue = "Basic \(encodedAuthData!)"
request.setValue(authValue, forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
return request
}
//I've implemented URLRequestConvertible 'Router'. it also have call the same above url
Alamofire.request(Router.Authorize(acc, pwd))
.response({(request, response, data, error) in
println(request)
})
But it's not worked and seem like turned to infinite loop. I tested on Charles.
Alternative (code snippet) solution using AlamoFire 2.4 (Xcode7). In my case, I always expect a redirect. (I am unpacking a shortened link.) If the completion in the request.response call runs, that is an error to me.
func printRedirectUrl() {
// taskWillPerformHTTPRedirectionWithCompletion: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse, NSURLRequest, NSURLRequest? -> Void) -> Void)?
Alamofire.Manager.sharedInstance.delegate.taskWillPerformHTTPRedirectionWithCompletion = { session, task, response, request, completion in
// request.URL has the redirected URL inside of it, no need to parse the body
print("REDIRECT Request: \(request)")
if let url = request.URL {
print("Extracted URL: \(url)")
}
Alamofire.Manager.sharedInstance.delegate.taskWillPerformHTTPRedirection = nil // Restore redirect abilities
return
}
// We expect a redirect, so the completion of this call should never execute
let url = NSURL(string: "https://google.com")
let request = Alamofire.request(.GET, url!)
request.response { request, response, data, error in
print("Logic Error, response should NOT have been called for request: \(request)")
Alamofire.Manager.sharedInstance.delegate.taskWillPerformHTTPRedirection = nil // Restore redirect abilities - just in case
}
}
REDIRECT Request: { URL: https://www.google.com/ }
Extracted URL: https://www.google.com/
In Swift 4,
let delegate = Alamofire.SessionManager.default.delegate
delegate.taskWillPerformHTTPRedirection = { (session, task, response, request) -> URLRequest? in
// print("REDIRECT Request: \(request)")
return nil
}
Hello its actually pretty simple
Alamofire has a redirector that will
Example
let request = AF.request("https://google.com",method: .post,parameters: parameters)
.cURLDescription { description in
debugPrint(description)
}
let redirector = Redirector(behavior: .doNotFollow)
request.redirect(using: redirector)
with that it wont redirect
its also in the docs in the advanced usage section
It looks like returning nil can possibly cause a deadlock. Instead, try to create a new NSURLRequest with the same original URL. See #jhersh's notes in a previous Alamofire PR along with the comments and implementation in his tests.
How to Stop a Redirect
func disallowRedirect() {
let URL = "http://google.com/"
let delegate = Alamofire.Manager.sharedInstance.delegate
delegate.taskWillPerformHTTPRedirection = { session, task, response, request in
return NSURLRequest(URL: NSURL(string: URL)!)
}
let request = Alamofire.request(.GET, URL)
request.response { request, response, data, error in
println("Request: \(request)")
println("Response: \(response)")
println("Data: \(NSString(data: data as! NSData, encoding: NSUTF8StringEncoding))")
println("Error: \(error)")
}
}
disallowRedirect()
The fact that you cannot pass nil into the NSURLSessionTaskDelegate method's completionHandler looks like a bug. I'm going to file a radar for this and I'll post a link to the bug report once I'm finished.
I don't know if your version of Alamofire has a support for public delegate. Last time I checked delegate was private. I am using the changes made by #jhersh. You can check his additions and how to use delegate by followin github pr. https://github.com/Alamofire/Alamofire/issues/314

Network API to return HTTPS response in iPhone

Can I return the server response from any function to its calling function using any of the Network API like AFNetworking, MKNetworkKit etc, in iPhone.
Currently I am using httpGet function of NSURLRequest. I made the following function but I am not able to return the server response to it calling function. Please help me.
func connserv(jsonString:NSDictionary) -> NSDictionary{
var abc: NSDictionary?
// This is the action performed when clicked on the Connect button on the connectivity screen
println("------------------Function connserv")
let prefs = NSUserDefaults.standardUserDefaults()
var IP: AnyObject = prefs.objectForKey("IP")!
var port: AnyObject = prefs.objectForKey("Port")!
println("IP in Connection : \(IP)")
println("port in Connection : \(port)")
prefs.synchronize()
//var learn = LearnNSURLSession()
let localizedModel = UIDevice.currentDevice().localizedModel
let model = UIDevice.currentDevice().model
let devicesystemVersion = UIDevice.currentDevice().systemVersion
println("HTTP request jsonString : \(jsonString)")
var request = NSMutableURLRequest(URL: NSURL(string: "https://\(IP):\(port)/")!)
var response: NSURLResponse?
var error: NSError?
//println("HTTP request jsonString : \(jsonString)")
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(jsonString, options: nil, error: &err)
request.HTTPMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
// send the request
var learn = LearnNSURLSession()
println("HTTP request : \(request)")
learn.httpGet(request) {
(resultString, error) -> Void in
if error != nil
{
println("completion block")
}
else
{
let data = (resultString as NSString).dataUsingEncoding(NSUTF8StringEncoding)
var er: NSError?
let JSONdata: AnyObject = NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers,error: &er)!
let abc: AnyObject = NSJSONSerialization.JSONObjectWithData(data!, options: .MutableLeaves, error:&er)!
println("abc : \(abc)")
println("JSONdata : \(JSONdata)")
learn.callback(result: resultString, error: error)
}
}
//return abc!;
}
I do not know how your LearnNSURLSession class works, so I cannot suggest a solution with that. But here is how to find the returned headers and status, together with the returned data and any error information, from an NSURLConnection, documented here:
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.currentQueue(), completionHandler: {
response, data, error in
if let response = response as? NSHTTPURLResponse {
debugPrintln(response.allHeaderFields)
let statusCode = response.statusCode
debugPrintln("statusCode: \(statusCode): \(NSHTTPURLResponse.localizedStringForStatusCode(statusCode))")
} else { println("That's odd.") }
})
While that documentation suggests that the response is an NSURLResponse, this page makes clear that you will get a NSHTTPURLResponse which contains all the headers and status.
If you need a synchronous version, that will stop the function until it gets a result, you can use this call instead, like this:
var response:NSURLResponse?
var e: NSError?
NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &e)
debugPrintln(response)
That will allow you to return a meaningful result to the caller.