Get the URL response code before ViewController loads? - swift

I am trying to get the status code of the URL before the MainViewController loads, now I know walk arounds but is it doable? I tried putting the code in willEnterForeground, didFinishLaunchingWithOptions and on init(), with the last one working from time to time.
Before anyone asks or thinks as to why I might do this, I am mostly wondering now, is there a faster way to get the response code etc.
let task = URLSession.shared.dataTask(with: url!) { _, response, _ in
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
if httpResponse.statusCode == 404 {
print("404")
} else if httpResponse.statusCode == 200 {
print("200")
}
}
}

What you have to do is create a new view controller that will indicate that somethings is loading, then push/present the MainViewController on the success of the asynchronous method.
If you try to block the main thread until the status comes, the iOS watchdog will likely kill of your app. Source

Related

How to go back to DispatchQueue.main from URLSession.shared.dataTask (macOS framework)

I'm building a macOS framework and at some point, I need to make a request to some API
When I got the response I want to update the UI. I'm using URLSession.shared.dataTask to make the call and as I know the call is made in the background thread
For some reason when I try to go back to the main thread nothing happens
I'm using a virtual machine to run my framework
Any help?
Thanks
Here how I doing the request:
URLSession.shared.dataTask(with: request) { data, response, error in
if error != nil {
DispatchQueue.main.async {
//Display error message on the UI
//This never happens
//Never go back to the main thread
//Framework stop working
}
}
}.resume()
Are you sure that your task is called?
let dataTask = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
....
DispatchQueue.main.async {
completion(nil, nil)
}
}
dataTask.resume() // You should add this.

Swift - Network Requests and Background Queue

just wanted some clarification on the best practices to make network api calls in Swift 2.
Here is how my typical network requests looks like to download JSON data:
let session = NSURLSession(configuration: .defaultSessionConfiguration())
let url = NSURL(string: my_url_string)
let request = NSURLRequest(URL: url)
let dataTask = session.dataTaskWithRequest(request) { data, response, error in
do {
self.tableData = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! [NSDictionary]
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
} catch let error {
print(error)
}
}
dataTask.resume()
My question is: should I wrap all of this code block into a background queue? Should I do as follows:
let download_queue = dispatch_queue_create("download", nil)
dispatch_async(download_queue) { () -> Void in
previous code here
}
Or should I use one of the given high priority queues such as:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)
Also, when making additional network requests on subsequent view controllers, should I use the same queue I use here or should I create a new one?
By default NSURLSession API is highly asynchronous. Usefull information from the Apple docs.
There is no visible issues that indicate to wrap you're code block with GCD and also completion block runs on background thread so there is right usage of the GCD to update UITableview

View Dependent on network Request

In order to fully render my View from my View Controller, I need to have a response from a network request.
I have been trying to do this is many different ways, but have been unsuccessful each time.
Originally, I had gotten it to work by making a "synchronous" network request prior to calling any methods to render the View. However, the compiler is warning me that the synchronous network requests are deprecated as of ios 8.
What is the best way to accomplish this in the most performant way?
I have tried:
override func loadView() {
dispatch_sync(dispatch_get_main_queue()){
// GET the Markup
let url = NSURL(string: self.PageURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let RequiredViewData = session.dataTaskWithRequest(request) {(data, response, error) in
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
self.RequiredViewJSON = json
self.view = UIView(frame: UIScreen.mainScreen().bounds)
print(data)
} catch {
print("error serializing JSON: \(error)")
}
}
RequiredViewData.resume()
}
}
But that just makes my app render as a blank screen.
Essentially what I need to accomplish is this:
Make a network request and receive the response before any view rendering can occur.
Thanks in Advance!
I never really tried to override loadView nor know if you should, but I think what you need to do is call super in this case to get your view to render again.
Edit
Also per your comment I put the main thread call "after" you get the call back from the NSURLSession. I might have a } in the wrong spot but should get you close enough.
override func loadView() {
// GET the Markup
let url = NSURL(string: self.PageURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let RequiredViewData = session.dataTaskWithRequest(request) {(data, response, error) in
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments)
self.RequiredViewJSON = json
dispatch_sync(dispatch_get_main_queue()){
//You may also want to try commenting this out unless you are intentionally creating a blank view.
self.view = UIView(frame: UIScreen.mainScreen().bounds)
print(data)
//call super after you get what you need
super.loadView()
}
} catch {
print("error serializing JSON: \(error)")
}
}
RequiredViewData.resume()
}
}
Hopefully that helps.
The view controller should handle all of this networking logic in viewDidLoad or viewWillAppear, not in loadView. I'd suggest setting a loading state an initial empty state on the subview, then once you have what you need, update the view with that data. You may need to call setNeedsLayout on the view to update for the new data.

How to make network request faster in Swift

I'm using Alamofire for network request. When I load a new viewController I make a new request in ViewDidAppear to get example url to images ect. When I make the request in ViewDidAppear there is a delay before the data appear, I also tried in ViewDidLoad the request was a little bit faster, but you can stil see the data appear after a small delay. It is okay that when a user access the viewController first time the user will see the data is loading, but is there a way to keep the data so that when a user navigate away from the controller, example when a user go back from a push in a navigationController and then navigate forward again without making the request to get the data again?
Here is one of my request in ViewDidAppear.
Hope you guys can help - Thank you
override func viewDidAppear(animated: Bool) {
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
var parameters = [String: AnyObject]()
if(self.MySelf) {
parameters = ["userId": LoginViewController.CurrentUser.UserID as AnyObject]
}
else {
parameters = ["userId": self.UserID as AnyObject]
}
//GET posts
Alamofire.request(.POST, Config.ApiURL + "getUserPosts?token=" + LoginViewController.CurrentUser.Token, parameters: parameters as! [String : AnyObject]).responseJSON{ response in
print(response)
switch response.result {
case .Success(let data):
let json = JSON(data)
if let posts = json["post"]["data"].array {
self.postArray = posts
self.postArray = self.postArray.reverse()
self.navigationItem.title = json["user"]["firstname"].string! + " " + json["user"]["lastname"].string!
self.User = json["user"]
self.UserPic = self.User["photourl"].string!
}
else {
print("Array is empty")
}
case .Failure(let error):
print("Request failed with error: \(error)")
}
self.ProfilePostColView?.reloadData()
}
}
Try making the network request in an earlier view controller - say a loading screen and then pass the response to this view controller.
Alternatively you could store the response in a cache service of sorts - when the user navigates back to this view controller you could check it already in the cache if so load it up to the view if not call the request.
Also making the network request in viewDidLoad will be faster as it called before viewDidAppear - but keep in mind viewDidLoad is only called once for a specific instance of a viewController where as viewDidAppear is called every time that instance is displayed again (eg. if it as the bottom of the navigation stack and the user presses back to it).
Keep the user in mind - you do not want to be chewing up their data so if you know the request will have the same response you only want to make the http request once.

Correct asynchronous Authentication while keeping a responsive UI

What it's supposed to be
I have a username field. When the username is entered and the sendButton is clicked, the userdata is fetched with a asynchronousRequest as a JSON file.
After the sendButton is clicked, I want to display an ActivityIndicator.
The UI shall still be responsive, while the request is made.
How it is now
I click the sendButton and the UI freezes. Even the ActivityIndicator does NOT get displayed.
The code
LoginVC:
func buttonTouchedUpInside(sender: UIButton) {
toggleActivityIndicatorVisibilityOn(true)
LoginManager.sharedInstance.checkUserForCredentials(username: textFieldLogin.text, password: "")
toggleActivityIndicatorVisibilityOn(false)
}
func loginManagerDidFinishAuthenticationForUser(userData: [String:String]?){
// Delegate Method, which works with the fetched userData.
}
LoginManager
func checkUserForCredentials(#username: String ,password: String) -> Void {
let url = NSURL(string: "\(Config.checkCredentialsUrl)username=\(username)")
let request = NSURLRequest(URL: url!)
NSURLConnection.sendAsynchronousRequest(request, queue: .mainQueue()) { (response, data, error) -> Void in
if error != nil {
//Display error-message
}
var error : NSError?
let json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &error) as? [String:String]
self.delegate?.loginManagerDidFinishAuthenticationForUser(json)
}
}
In short: I want the request to be made in background, that the Activityindicator is shown and the UI stays responsive. After the asynchronous request successfully fetched the json, the delegate method shall be called
The second line of code in the buttonTouchedUpInside method, which reads LoginManager.sharedInstance.checkUserForCredentials(username: textFieldLogin.text, password: "") is calling an asynchronous function within it, which means that it is not blocking the next line of code... which is the one that (I am guessing) triggers your loading screen to become invisible again.
Basically, your loading screen is showing up, but it is immediately being hidden again. To fix at least the part with your loading screen, put the third line of code in the buttonTouchedUpInside function in the callback method loginManagerDidFinishAuthenticationForUser instead.