Running a url using swift - ios

I've created a script/api which is suppose to add a record to my database when running a specific url. However i'm not sure how to run this url. I do not expect anything back just to run this url? how can i do this?
var identifier = UIDevice.currentDevice().identifierForVendor.UUIDString
var addViewUrl = "http://url/addview.php?type=ios&identifier=\(identifier)&newsid=\(newsObject?.id)"

Based on my comment:
You should get a response and check for errors.
Also there is always the possibility to call a URL asynchronously to avoid blocking the GUI if the request takes a long time.
This can be made using delegate patterns or with completions handlers like in Objective-C.
Example:
var url = NSURL.URLWithString(addViewUrl)// Creating URL
var request = NSURLRequest(URL: url)// Creating Http Request
var queue: NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: queue, completionHandler:{(response:NSURLResponse!, responseData:NSData!, error: NSError!) -> Void in
if error != nil
{
println(error.description)
}
else
{
var responseStr:NSString = NSString(data:responseData, encoding:NSUTF8StringEncoding)
//Everything went fine
}
})

Related

How to check if server is responding in Swift 5

I'm looking for a Swift 5 version of something like the fileExists function (below). For context I have an app that is heavily dependent on interaction with a remote server. For resilience we have created a second remote server and want the app to check if the first one is available, and if not, use the second one. I have found possible solutions using URLSession.shared.dataTask but they all just display a message rather than return an alternative url to use. Any suggestions would be most welcome.
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
}

Async call within Async call not executed

I'm trying to retrieve the XML from an rss feed, get the links for each article, and then extract info from those articles. I'm using AEXML to get the xml, and ReadabilityKit for link extraction.
I'm successfully pulling the links from the XML, but the parser call on Readability is never executing. I don't want this on the main thread as it blocks all UI, but so far that's the only way I've made it work. Code is below (removed that dispatch get main queue):
func retrieveXML() {
let request = NSURLRequest(URL: NSURL(string: "<XML URL HERE>")!)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
(data, response, error) in
if data == nil {
print("\n\ndataTaskWithRequest error: \(error)")
return
}
do {
let xmlDoc = try AEXMLDocument(xmlData: data!)
for child in xmlDoc.root.children {
if let postURL = child["id"].value {
let url = NSURL(string: postURL)
let parser = Readability(url: url!)
let title = parser.title()
print("TITLE: \(title)")
}
}
} catch {
print(error)
}
}
task.resume()
}
Thanks for reporting. The new version is available in cocoa pods and cartage with a new aync API. Sync API is removed from the project.
Readability.parse(url: articleUrl, { data in
let title = data?.title
let description = data?.description
let keywords = data?.keywords
let imageUrl = data?.topImage
let videoUrl = data?.topVideo
})
Thanks for your contribution! For more info please check README https://github.com/exyte/ReadabilityKit
The problem is that Readability is deadlocking. You're calling it from a NSURLSession completion block (which defaults to a serial queue), but Readability blocks that queue with a semaphore until its own network request is completed. So Readability is deadlocking because it's blocking a thread waiting for a semaphore signal that is supposed to be sent from the same thread it is blocking.
You can fix this by asynchronously dispatching the code that instantiates Readability to a separate queue (e.g. a global queue).
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)) {
let url = NSURL(string: postURL)
let parser = Readability(url: url!)
let title = parser.title()
print("TITLE: \(title)")
}
It looks like that API has been updated to run asynchronously, so get the latest version and this deadlocking issue is eliminated and the above asynchronous dispatch will not be needed. You'll obviously have to employ the completion handler pattern of the updated API.

Checking HTTP Status code without cache

I need to check if a website is reachable before loading it. I am new in iOS developement but this is the method I've implemented to discover the response.
var url = NSURL(string: "http://www.apple.com")
var task = NSURLSession.sharedSession().dataTaskWithURL(url!) {
data, response, error in
println(data)
var httpResponse = response as? NSHTTPURLResponse
println(httpResponse)
}
task.resume()
It works! But the problem is that the response comes from the cache... So the result is that:
If I am checking if a file exists and at that moment I am checking it exists -> for the application it will always exist because it is stored in the cache... So if I remove the file and then I make the request... it will always give me response 200 and not 404.
Infact if I insert this line of code (it deletes the cache!) before making the request then it works like it should work and it always check for real if the website or the file exists!
NSURLCache.sharedURLCache().removeAllCachedResponses()
So... how can I solve this problem in Swift?...thank you very much
You can set a no cache policy by using a new url session instance.
Create a property and set a new NSURLSession instance to it.
var urlSession : NSURLSession!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.requestCachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData;
self.urlSession = NSURLSession(configuration: configuration)
Use this URLSession property to get your data.
var url = NSURL(string: "http://www.apple.com")
var task = self.urlSession.dataTaskWithURL(url!) {
data, response, error in
// Your code
}
task.resume()

save session in http request swift

in my app I'm using JSON and I made a session recently so if I would like to make some http request to get data for a specific user, the user must log in before (also used by http request).
in the safari when I entering the url's of login and then the url of receive data, it does that as needed.
but in my app, I first call login and then the url for getting data, but it's probably starting a new session in every url request which leads me to get an error and not receive the data.
my url request function is:
static func urlRequest (adress: String, sessionEnded: (NSDictionary->Void)?){
println(adress)
var urli = NSURL(string: adress)
var request = NSURLRequest(URL: urli!)
var rVal = "";
self.task = NSURLSession.sharedSession().dataTaskWithURL(urli!) {(data, response, error) in
var parseError: NSError?
let parsedObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.AllowFragments,
error:&parseError)
let po = parsedObject as NSDictionary
if let a = sessionEnded{
sessionEnded!(po)
}
}
task!.resume()
}
thanks in advance!!
You have shared only half of the puzzle with us, the client code. We can't comment on why the app isn't working with a clearer picture of what the server API. For example, once you "log in", how do subsequent queries confirm that the request is coming from valid session. Furthermore, you report that "every url request which leads me to get an error". Well, what error do you receive? You have to be far more specific regarding the precise errors/crashes you are receiving. BTW, are you logging on to some service with a well-defined API or are you writing that code yourself, too?
Having said that, I might suggest a few refinements to this method:
The sessionEnded (which I've renamed completionHandler to conform to informal standard naming conventions), probably should return an optional NSError object, too, so the caller can detect if there was an error.
Your unwrapping of the sessionEnded completion handler can be simplified to use ?.
When you parse the object, you should feel free to perform the optional cast, too.
You probably want to detect a network error (in which case data would be nil) and return the network NSError object.
Minor point, but I'd probably also rename the function to conform to Cocoa naming conventions, using a verb to start the name. Perhaps something like performURLRequest.
This is your call, but I'd be inclined to have the method return the NSURLSessionTask, so that the caller could use that task object if it wanted to (e.g. save the task object so that it could cancel it later if it wanted to).
Thus, that yields something like:
func performURLRequest (address: String, completionHandler: ((NSDictionary!, NSError!) -> Void)?) -> NSURLSessionTask {
let url = NSURL(string: address)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
if data == nil {
sessionEnded?(nil, error)
} else {
var parseError: NSError?
let parsedObject = NSJSONSerialization.JSONObjectWithData(data, options: nil, error:&parseError) as? NSDictionary
completionHandler?(parsedObject, parseError)
}
}
task.resume()
return task
}
And you'd invoke it like:
performURLRequest("http://www.example.com/some/path") { responseDictionary, error in
if responseDictionary == nil {
// handle error, e.g.
println(error)
return
}
// use `responseDictionary` here
}

NSURLConnection Asynchronous Request inside a for-loop does not properly download NSData (Swift)

I've written a class called Movie whose initializer takes an integer "id" to retrieve data from the Rotten Tomatoes API:
init(id: Int) {
let movieURL = NSURL(string: "http://api.rottentomatoes.com/api/public/v1.0/movies/\(id).json?apikey=\(apiKey)")!
NSURLConnection.sendAsynchronousRequest(NSURLRequest(URL: movieURL), queue: NSOperationQueue()) { (response, movieData, error) -> Void in
var movieJson = NSJSONSerialization.JSONObjectWithData(movieData, options: NSJSONReadingOptions.MutableContainers, error: nil) as? [String: AnyObject]
self.id = self.idFromMovieJson(movieJson)
self.title = self.titleFromMovieJson(movieJson)
// ...
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate!.movieDidDownload(self)
})
}
}
If I instantiate a Movie object with a correct id, everything goes as expected. This is what I've written in another class:
var movie = Movie(id: 771351912)
movie.delegate = self
func movieDidDownload(movie: Movie) {
println(movie.title)
}
And this is the output:
Optional("Interstellar")
However, when I try to instantiate a Movie object inside a for-loop like this:
let ids = [771351912, 771380953, 771041011, 13863, 12490, 771311818, 771321699, 11691]
for id in ids {
var movie = Movie(id: id)
movie.delegate = self
}
The print results are not very encouraging:
Optional("Super 8")
Optional("Interstellar")
Optional("Pulp Fiction")
nil
Optional("The Nightmare Before Christmas")
nil
nil
Optional("Nightcrawler")
Worst of all, the println() output is different each time I build and run my code:
Optional("Interstellar")
Optional("Pulp Fiction")
Optional("Super 8")
Optional("Nightcrawler")
Optional("The Nightmare Before Christmas")
nil
nil
nil
I even tried to create different NSOperationQueues with different names to use in the NSURLConnection.sendAsynchronousRequest() method but that didn't work out too:
var queue = NSOperationQueue()
queue.name = "\(id)"
I guess that the problem is related to the fact I'm sending too many requests at the same time. I've placed sleep(1) in the ids for-loop and it actually prints the movie titles properly.
Does anybody know how to asynchronously make multiple requests inside a for-loop?
The swift println() function is asynchronous, and the order of delivery isn't guaranteed, so you may get all kinds of confusing results if you use it like this.
I'd suggest switching to using NSLog() instead, as that will give more consistent results.
Also, creating a new throwaway NSOperationQueue() for each request seems like a bad idea, as the queue may get released before the operation executes. Try using NSOperationQueue.mainQueue() instead.
As an added benefit, using a single queue instead of a different queue for each request should ensure that the movies get downloaded in the order you've requested them.

Resources