stop urlRequest onViewDisappear - swift

I load my json info from my server as follows, but when I click away to a different page too soon, the request keeps trying in the background and there is a warning that the viewController can't be found anymore. How would I cancel all requests onViewDisappear?
if let requestURL = URL(string: "https://www.example.com/file.php") {
var urlRequest = URLRequest(url: requestURL)
urlRequest.httpMethod = "POST"
let postString = "email=\(loginUsername.text!)"
urlRequest.httpBody = postString.data(using: .utf8)
let session = URLSession.shared
let task = session.dataTask(with: urlRequest as URLRequest) { (data, response, error) in
if let data = data {
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
// Async Stuff
DispatchQueue.main.async{
// do things
}
DispatchQueue.main.async(execute: {
})
}
} catch {
print("Error: \(error)")
}
}
}
task.resume()
}

Save your requests somewhere and then on moving away from the controller call:
task.cancel()

class DataCall {
var task: URLSessionDataTask?
func load() {
guard let requestURL = URL(string: "https://www.example.com/file.php") else { return }
var urlRequest = URLRequest(url: requestURL)
urlRequest.httpMethod = "POST"
let postString = "email=\(loginUsername.text!)"
urlRequest.httpBody = postString.data(using: .utf8)
let session = URLSession.shared
task = session.dataTask(with: urlRequest as URLRequest) { (data, response, error) in
if let data = data {
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
// Async Stuff
DispatchQueue.main.async{
// do things
}
}
} catch {
print("Error: \(error)")
}
}
}
task.resume()
}
func stopTask() {
task.cancel()
}
}
Then in your viewWillDissapear you call dataCall.stopTask().

Related

Swift POST Request Method Not Allowed

I Use Laravel as backend and I have below route to verify the users
$router->post('SignIn','Api\V1\UserProfileController#SignIn');
I have tested this route many time using postman and its working fine, now i want to send post request from my app using below request
let url = URL(string: "http://192.168.xxx.xxx/BARI/public/Api/V1/Verify")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let param : [String : Any] = ["ph_number" : userDefaults.string(forKey: "ph_number")!, "code" : smsNumberTF.text!]
request.httpBody = try? JSONSerialization.data(withJSONObject: param, options: [])
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = TimeInterval(30)
configuration.timeoutIntervalForResource = TimeInterval(30)
let session = URLSession(configuration: configuration)
let task = session.dataTask(with: url) { (data, urlResponse, error) in
if(error != nil){
DispatchQueue.main.async {
self.progress.stopAnimating()
self.isLoading = false
// show connection error alert
print("connection error : \(error?.localizedDescription)")
}
}else{
let outputStr = String(data: data!, encoding: String.Encoding.utf8) as String?
print(outputStr)
DispatchQueue.main.async {
do {
self.progress.stopAnimating()
self.isLoading = false
let jsonData = try JSONDecoder().decode(BasicResponse.self, from: data!)
if(jsonData.statusCode == 1000){
// let userDefaults = UserDefaults.standard
// userDefaults.set("+964" + self.phoneET.text!, forKey: "contact_number")
// let vc = Verfiy()
// self.navigationController?.pushViewController(vc, animated: true)
}else{
//self.alert.show(target: self.view, message: jsonData.message!)
}
}
catch let jsonerr {
print("error serrializing error",jsonerr)
}
}
}
}
task.resume()
But Im getting Method Not Allowed response back? what Im missing her!?
Any Help will be much appreciated

URLSession.shared.dataTask block not running

I'm trying to make a request to an API. Without the completion handler, the request proceeds without issue. However, now that I've added in a completion handler, the URLSession.shared.dataTask block doesn't appear to run at all; the second print statement never prints. How should I adjust my code to fix this issue?
func makeRequestToApi(word: String, completionHandler: #escaping ([String]) -> Void) {
var array = [String]()
let appId = ""
let appKey = ""
let language = "en-us"
let unwrappedURL = URL(string: "https://od-api.oxforddictionaries.com:443/api/v2/entries/\(language)/\(word)")!
var request = URLRequest(url: unwrappedURL)
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue(appId, forHTTPHeaderField: "app_id")
request.addValue(appKey, forHTTPHeaderField: "app_key")
print("THIS STATEMENT PRINTS...")
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
print("AND THIS STATEMENT")
if let data = data,
let _ = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) {
do {
let root = try JSONDecoder().decode(Root.self, from: data)
let results = root.results
for result in results {
for lexical in result.lexicalEntries {
for entry in lexical.entries {
for sense in entry.senses {
for example in sense.examples {
array.append(example.text)
}
}
}
}
}
} catch {
print(error)
}
completionHandler(array)
}
}
dataTask.resume()
}
This is the function that I used on another view controller that returned data for the same words:
func makeRequestToApi() {
let appId = ""
let appKey = ""
let language = "en-us"
let strictMatch = "false"
var word = search
word = word.lowercased()
let oxDicURL = URL(string: "https://od-api.oxforddictionaries.com:443/api/v2/entries/\(language)/\(word)?strictMatch=\(strictMatch)")
if let unwrappedURL = oxDicURL {
var request = URLRequest(url: unwrappedURL)
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue(appId, forHTTPHeaderField: "app_id")
request.addValue(appKey, forHTTPHeaderField: "app_key")
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let data = data,
let _ = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) {
do {
let root = try JSONDecoder().decode(Root.self, from: data)
let results = root.results
for result in results {
for lexical in result.lexicalEntries {
for entry in lexical.entries {
for sense in entry.senses {
for example in sense.examples {
print(example.text)
self.array.append(example.text)
}
}
}
}
}
self.search = word
DispatchQueue.main.async() {
self.performSegue(withIdentifier: "goToDetail", sender: nil)
}
} catch {
print(error)
}
}
}
dataTask.resume()
}
}

Swift - Multiple URL Request - Code To Refactor and To Reuse

I'm new to Swift and I am trying to refactor my URL Post requests. I have multiple URL POST requests inside the same View Controller like this. Everything works fine but it seems to me that there is a lot of repetitive code that could be reused. Particularly, I don't know how to pass/handle different Data Models that should be used in parseRequest1 and parseRequest2. I also read that there should be only one session used for URL requests within the same project. Any help would be greatly appreciate it!
func request1() {
let parameters = [...//some parameters to send]
guard let url = URL(string: "https//www.....") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let parametersToSend = try? JSONSerialization.data(withJSONObject: parameters, options: [])
else {
print("Error")
return
}
request.httpBody = parametersToSend
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let safeData = data {
self.parseRequest1(data: safeData)
}
}.resume()
}
func parseRequest1(data: Data){
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(DataModelForRequest1.self, from: data)
DispatchQueue.main.async {
self.performAction1(request1Result)
}
} catch {
print(error)
}
}
Then I have another URL request request2 which is almost identical except the parameters, and model to be used for decoding and action inside parseRequest2.
func request2() {
let parameters = [...//some parameters to send]
guard let url = URL(string: "https//www.....") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let parametersToSend = try? JSONSerialization.data(withJSONObject: parameters, options: [])
else {
print("Error")
return
}
request.httpBody = parametersToSend
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let safeData = data {
self.parseRequest2(data: safeData)
}
}.resume()
}
func parseRequest2(data: Data){
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(DataModelForRequest2.self, from: data)
DispatchQueue.main.async {
self.performAction2(request2Result)
}
} catch {
print(error)
}
}
The only differences seem to be:
request parameters
type of model returned
the action you do after the response is received
This means that we can write this as one single method taking the above three values as parameters:
func request<T: Codable>(modelType: T.Type, parameters: [String: Any], completion: (T) -> Void) {
func parseResponse(data: Data){
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(T.self, from: data)
DispatchQueue.main.async {
completion(decodedData)
}
} catch {
print(error)
}
}
guard let url = URL(string: "https//www.....") else {return}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let parametersToSend = try? JSONSerialization.data(withJSONObject: parameters, options: [])
else {
print("Error")
return
}
request.httpBody = parametersToSend
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let safeData = data {
parseResponse(data: safeData)
}
}.resume()
}
You can then call this method with the appropriate parameters as per your needs.

How to wait for a download task to finish in swift 3

I am trying to build a user regeistration form, which should check if the user already exists. So I am sending a php request to my my mysql server. If the return value is empty, the user does not exists yet.
Unfortunatley I am really stuck with waiting for this check to finish. I tried several solutions I found googleing but none of them worked. My current code uses semaphores and will crash with "fatal error: unexpectedly found nil while unwrapping an Optional value", so the semaphore is not waiting until the task is finished as I would expect it.
Any hints, would be greatly appreciated. Thanks guys.
private func isUniqueEmail(email: String) -> Bool {
var result: Bool?
let semaphore = DispatchSemaphore(value: 1)
let requestURL = URL(string: "http://localhost/firstpostget/functions/get.php")
var request = URLRequest(url: requestURL!)
request.httpMethod = "POST"
let postParameters = "email=" + email
request.httpBody = postParameters.data(using: .utf8)
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) {(data, response, error) in
var myJson: AnyObject
do{
myJson = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if myJson.count == 0{
result = true
semaphore.signal()
} else{
result = false
semaphore.signal()
}
} catch{
//TODO
print(error)
}
}
task.resume()
semaphore.wait(timeout: .distantFuture)
return result!
}
Your task is async and you are force unwrapping nil value so this is the reason it crashes.
You have to change your function implementation to also be async, for example using closures:
private func isUniqueEmail(email: String, completion: ((Bool) -> (Void))?) {
let requestURL = URL(string: "http://localhost/firstpostget/functions/get.php")
var request = URLRequest(url: requestURL!)
request.httpMethod = "POST"
let postParameters = "email=" + email
request.httpBody = postParameters.data(using: .utf8)
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) {(data, response, error) in
var myJson: AnyObject
do{
myJson = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if myJson.count == 0 {
completion?(true)
} else{
completion?(false)
}
} catch{
//TODO
print(error)
}
}
task.resume()
}
Now you can use this function in this way:
isUniqueEmail(email: "aaa#bbbb.com") { result in
if result {
print("email unique")
} else {
print("email not unique")
}
}
I think you should rethink the pattern you're using to get the data out of your request, you should consider using a custom handler/callback method that you pass along with the email you're trying to check. See below for an example:
private func isUniqueEmail(email: String, handler: ((_ result: Bool) -> Void)?) -> Void {
let requestURL = URL(string: "http://localhost/firstpostget/functions/get.php")
var request = URLRequest(url: requestURL!)
request.httpMethod = "POST"
let postParameters = "email=" + email
request.httpBody = postParameters.data(using: .utf8)
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) {(data, response, error) in
var myJson: AnyObject
var result: Bool = false
do{
myJson = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
if myJson.count == 0 {
result = true
}
guard handler != nil else {
return
}
handler!(result)
} catch{
//TODO
print(error)
}
}
task.resume()
}
Run:
isUniqueEmail(email: "test#test.com", handler: { result in
print(result) // true || false
})
If you really want to go down the "wait" route then take a took at DispatchGroup's
https://developer.apple.com/documentation/dispatch/dispatchgroup
try using this:
ApihelperClass
static let sharedInstance = ApihelperClass()
typealias CompletionHandler = (_ success:Bool, _ error:Bool, _ result:NSDictionary) -> Void
typealias ErrorHandler = (_ success: Bool, _ error:Bool) -> Void
func callPostRequest(_ urlPath: String, params:[String: AnyObject], completionHandler: #escaping CompletionHandler, errorHandler:#escaping ErrorHandler ){
print("urlPath:==> \(urlPath) ")
let session = Foundation.URLSession.shared
let url = URL(string: urlPath)
var request = URLRequest(url : url!)
request.httpMethod = "POST"
do {
let jsonData = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
session.dataTask(with: request, completionHandler: { data, response, error in
OperationQueue.main.addOperation {
guard error == nil && data != nil else { // check for fundamental networking error
print("error=\(error)")
errorHandler(false, true)
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
// print("response = \(response)")
}
let responseString = String(data: data!, encoding: String.Encoding.utf8)
print("responseString = \(responseString!)")
if let responsedata = responseString!.data(using: String.Encoding.utf8)! as? Data{
do {
let jsonResult:NSDictionary = try JSONSerialization.jsonObject(with: responsedata, options: []) as! NSDictionary
print("Get The Result \(jsonResult)")
//parse your jsonResult as per your requirements
if error != nil {
print("error=\(error)")
completionHandler(false, true, jsonResult)//
}
if let str = jsonResult["success"] as? NSNull {
print("error=\(str)")
completionHandler(false, true, jsonResult)
}
else {
let responseString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
// print("Response string : \(responseString)")
completionHandler(true, false, jsonResult)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
}) .resume()
}catch {
print("Error ->Catch")
}
}
Add to your viewcontroller
func isUniqueEmail(email: String){
ApihelperClass.sharedInstance.callPostRequest("http://localhost/firstpostget/functions/get.php", params: ["email":email as AnyObject], completionHandler: { (success, error, result) in
//success 200
}) { (success, error) in
//error
}
}
you can use URlSession like :
func isUniqueEmail(email: String,completion: #escaping (Bool) -> ()) {
var request = URLRequest(url: URL(string: "http://localhost/firstpostget/functions/get.php")!)
request.httpMethod = "POST"
let postString = "email=\(email)"
request.httpBody = postString.data(using: .utf8)
// loading to wait request
UIApplication.shared.isNetworkActivityIndicatorVisible = true
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// we get request request
UIApplication.shared.isNetworkActivityIndicatorVisible = false
guard let data = data, error == nil else { // check for fundamental networking error
print("error=\(String(describing: error))")
completion(false)
return
}
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(String(describing: response))")
completion(false)
}else{
completion(true)
}
let responseString = String(data: data, encoding: .utf8)
print("responseString = \(String(describing: responseString))")
}
task.resume()
}
and used in code Like
self.isUniqueEmail(email: "your Email") { (isExit) in
if isExit {
}else{
}
}
Ok, I just found a solution. My semaphore approach actually worked as well as dispatchgroups. The task just needed to be URLSession.shared.dataTask
Still thank's a lot for all the answers.

Why does post request work much faster from the second call?

I launch the app. Press the button which call this function, wait 2-3 seconds and than get the JSON. Then I press it again, wait 0-1 and then I get the JSON. Can you explain me the reason why it's happening and how can I avoid it?
public func serverUserRegister(userPhone: String?, userEmail: String?, completionHandler: #escaping ((String) -> ())) {
if let phone = userPhone {
if let email = userEmail {
let url = URL(string: "https://example.com/service.php")!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let paramString = "phone=\(phone)&email=\(email)"
request.httpBody = paramString.data(using: .utf8)
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
guard let _: Data = data, let _: URLResponse = response, error == nil else {
return
}
if let json = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary {
if let jsonStatus = json?["status"] as? String {
completionHandler(jsonStatus)
}
}
}
task.resume()
}
}
}