I’m alamofire request, and I found the request is sent twice. Is it an alamofire bug?
I tried to put a breakpoint, but it's not called twice and there is no button call this function twice or trigging twice.
Here is my code
ARSLineProgress.show()
guard let url = URL(string: "https://laghmat.kbmediawebites.com/api/rest/account") else {return}
let headers = ["X-Oc-Merchant-Id": MerchantId, "X-Oc-Session": K_Defaults.string(forKey: "sessionID"),"ACCEPT":"application/json"]
let params = [
"firstname": firstName,
"lastname": lastName,
"email": email,
"telephone": phone
] as! [String : String]
Alamofire.request(url, method: .put, parameters: params,encoding: JSONEncoding.default, headers: headers as! HTTPHeaders).responseJSON {
response in
switch response.result {
case .success:
print(response)
guard let data = response.data else { return }
do {
let value = try JSONDecoder().decode(GeneralResponse.self, from: data)
if value.success == 1 {
ARSLineProgress.showSuccess()
var user = GlobalHelpers.getUser()
user?.firstname = firstName
user?.lastname = lastName
user?.telephone = phone
user?.email = email
GlobalHelpers.setUser(user)
self.dismiss(animated: true, completion: nil)
} else {
ARSLineProgress.showFail()
self.alert(msg: LanguageManger.localize(word: "serverError"))
}
} catch let error {
ARSLineProgress.showFail()
self.alert(msg: LanguageManger.localize(word: "serverError"))
print(error)
}
break
case .failure(let error):
ARSLineProgress.showFail()
self.alert(msg: LanguageManger.localize(word: "serverError"))
print(error)
}
}
well I recommend you that lock all the buttons in the view, when the request is made.
for viewW in view.subviews
{
if let buttonV = viewW as? UIButton
{
buttonV.isUserInteractionEnabled = false
}
}
obviously, you have to lock at the beginning and unlock at the end.
if this isn't works, put a breakpoint in this line
switch response.result {
the first time, you will see that you don't have a status code, because the server not yet responding, then press next step, and you will see a status code, because the server already responds to you.
that doesn't mean the request is made twice, I think that this happen because the handshake is happening.
Related
During resistration of a user, i am uploading some photoes to the server. The first one is the profile photo. And the rest photoes i want to upload after the resistration process once completed. Means i want to upload the photo after getting the userid and the access token.Means after a successfully image upload, the user will enter to the "matchesViewController" and then only the upload process will start. Also i want to upload these photoes in background thread. Also if the app rinning in background then also the upload process should be continue. All the uploading process is done by Alamofire.
By writting the following code the photos are uploading successfully, but it is taking more time. Which is not a good user experience.
Also please tell me how to continue the upload process when the app running in back ground thread.
Also if the code is not good please help me to modify any changes in code if needed.
My code is: -
private func handleRegistration (_ parameterDict : [String : Any]){
let url = USER_REGISTER_URL
let headers: HTTPHeaders = [
"Content-Type": "application/x-www-form-urlencoded"
]
Alamofire.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameterDict {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
}
let random = randomString(length: 7)
multipartFormData.append(self.selectedProfilePic.image!.jpegData(compressionQuality: 0.4)!, withName: "fileset",fileName: "\(random).jpg", mimeType: "image/jpg")
}, usingThreshold: UInt64.init(), to: url, method: .post, headers: headers) { (result) in
switch result{
case .success(let upload, _, _):
upload.responseJSON { response in
print(response)
var userId: String?
var token: String?
if let result = response.result.value {
let JSON = result as! NSDictionary
guard let accessToken = JSON["access_token"] as? String
else{ return }
guard let uid = JSON["User_Id"] as? String
else{return}
userId = uid
token = "bearer " + accessToken
UserDefaults.standard.set(Int(uid), forKey: "User_Id")
UserDefaults.standard.set(token, forKey: "access_token")
let phone = JSON["Phone_Number"] as? String
let FirstName = JSON["FirstName"] as? String
let email = uid + "#gmail.com"
let tempDictionary : Dictionary = [kFIRSTNAME : FirstName!, kLASTNAME : "", kFULLNAME : FirstName!, kPHONE : phone!, kREGISTERUSEID: userId, kEMAIL: email] as [String : Any]
self.checkDeviceTokenAvailibility(uid: Int(userId!)!)
self.startRegistrationWithFirebase( detailDict: tempDictionary)
let storyBoard = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MainTabbarView")
storyBoard.modalPresentationStyle = .fullScreen
self.present(storyBoard, animated: true, completion: nil)
if let message = JSON["Message"] as? String{
if message == "Please Upload a image."{
}
}
}
if userId != nil && token != nil{
self.multiImageUpload(userId: Int(userId!)!, token: token!)
}
print("Succesfully uploaded")
if let err = response.error{
// onError?(err)
// print(err.localizedDescription)
return
}
// onCompletion?(nil)
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
// onError?(error)
}
}
}
One way to make the upload time a lot less is to send multiple parts at once using a for loop and using Alamofire to send a specific part. That way, it breaks it up into parts that take a lot less time to send. The con is that on the server side you will have to reconstruct the image.
Also if the code is not good please help me to modify any changes in code if needed.
It seems you have a lot of arbitrary space in your code.
I have an api manager class in my swift application and it has a server login with username and password.
I want to know how to create a completion handler for it that when the server responses with 200 status code, the function handles that response and for example performs a segue in the viewcontroller.
I did not find any tutorials for this. Thanks for your help!
EDIT 1:
What i need is: The completion handler is immediately run when the function is called. I want the completion handler run after server responds.
And this is my login function:
public class func Login(username: String, password: String, complitionHandler: #escaping (Int) -> Void) {
let urlS = "http://server.com/" + "login.php"
let url = URL(string: urlS)
var request = URLRequest(url: url!)
request.httpMethod = "POST"
let body = "username=\(username.lowercased())&password=\(password)"
request.httpBody = body.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data, error == nil else {
print(error!)
print("error")
logedIn = 2
return
}
do{
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary
if let parseJson = json {
let code = parseJson["status"] as! String
if code == "200" {
print("loged inn")
logedIn = 1
}else if code == "400" {
print("uuuser/pass error")
logedIn = 0
}
}
}catch{
print("json error")
logedIn = 2
}
}
task.resume()
DispatchQueue.main.async {
complitionHandler(logedIn)
}
}
And how i call the function in my ViewController:
Manager.Login(username: "1", password: "1") { (i) in
switch i {
case 0:
print("user/pass error")
case 1:
print("loged in")
self.performSegue(withIdentifier: "toMain", sender: self)
case 2:
print("json error")
default:
()
}
}
You have all of the pieces in place. You just need to move your call to the completion handler to the correct place:
}catch{
print("json error")
logedIn = 2
}
DispatchQueue.main.async {
complitionHandler(logedIn)
}
}
task.resume()
Also note that method names should start with lowercase letters so your Login function should be named login.
Now you can use this login method like:
login(username: someUsername, password: somePassword) { (result) in
if result == 1 {
// success - do your segue
} else if result == 0 {
// bad username/password
} else {
// some error
}
}
Alamofire.request(NEWS_FEED_URL).responseJSON { response in
guard let newsResponse = response.result.value as? [[String:String]] else{
print("Error")
return
}
print("JSON: \(newsResponse)")
This is my alamofire code to get response from server. Most of the time it is working fine but sometime it goes failure and print Error. Even if I paste print("JSON:", newsResponse) in failure block it shows the response but it is not going to the success block. I also print the status code once. Its giving me 200. My Internet is working good, the related url is giving response in postman. But sometimes it is not working why?
Try this method
func apiRequest(method:String, urlMethod:String, parametersDictionary:NSMutableDictionary, success:#escaping successDictionaryBlock, failure: #escaping failBlockErrorMessage){
let requestUrl = "Request URL"
Alamofire.request(requestUrl, method: .post, parameters: (parametersDictionary as NSDictionary) as? Parameters , encoding: JSONEncoding.default, headers: nil).responseJSON { response in
print(response)
print(requestUrl)
if(response.result.error == nil){
if((response.response?.statusCode)! < 500){
if(response.response?.statusCode == 200){
if let JSON = response.result.value {
print(JSON)
let dict = JSON as! NSDictionary
let status :Bool = dict["status"] as! Bool
if(status){
success(dict)
}else{
failure(dict["message"] as! String)
}
}
}else{
failure("Something went wrong please try again")
}
}else{
failure("Something went wrong please try again")
}
}
}
}
I am working on user authentication process but i stuck in the moment when reciving data from rest with token. Whenever i create the new task it does not enter on the first time into the function but after creating it skipping doing smth else which is showing a next hooked up UIViewController to segue.
My rest service with post method hashing user password, creating json, URL request and at the end creating URLSession. How could i wait for finish of this task ? To not let to do anything else before it is not complited ?
EDIT
I've added OpeartionQueue to liquidate nil's from next view.
func postLogin(name:String, pass:String, completion: #escaping (Bool) -> () ) {
let md5Data = self.MD5(string:pass)
let hashPass = md5Data!.map { String(format: "%02hhx", $0) }.joined()
let json: [String: Any] = ["username": name,
"passwordHash": hashPass ]
let jsonData = try? JSONSerialization.data(withJSONObject: json)
// create post request
let url = URL(string: LOGIN_URL)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// insert json data to the request
request.httpBody = jsonData
request.setValue("application/json;charest=utf-8", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
let message:String = responseJSON["message"] as! String
if !(message.range(of: "ERROR") != nil){
SessionMenager.Instance.token = message
completion(true)
}
} else{
print(error.debugDescription)
}
}
task.resume()
}
Then simply in my LoginViewController action with button :
#IBAction func LoginButton(_ sender: Any) {
let username = usernameTextField.text
let password = passwordTextField.text
if username == "" {
AlertWindow(title: "Username", message: "Wrong username")
} else if password == "" {
AlertWindow(title: "Password", message: "Wrong password")
} else {
let usernameToUpper = username!.uppercased()
RestService.Instance.postLogin(name: usernameToUpper, pass: password!, completion: { sth in
if sth {
OperationQueue.main.addOperation {
[weak self] in
self?.performSegue(withIdentifier: "mapSegue", sender: self)
}
} else {
return
}
})
}
}
The segue was hooked up into LoginButton which took me instantly to the next page. I've changed it into hooking up all view controllerr.
Thanks!
Because your segue is hooked up into LoginButton, it will automatically show the next viewController once you press the button.
Just hoop up the segue to the whole viewController and it should work.
I am building an swift app to interact with an MDM API to do large numbers of updates via PUT commands, and I am running in to issues with how to handle the massive numbers of API calls without overloading the servers.
I am parsing through a CSV, and each line is an update. If I run the commands asynchronously, it generates and sends ALL of the API calls immediately, which the server doesn't like.
But if I run the commands synchronously, it freezes my GUI which is less than ideal, as the end user doesn't know what's going on, how long is left, if things are failing, etc.
I have also tried creating my own NSOperation queue and setting the max number of items to like 5, and then putting the synchronous function in there, but that doesn't seem to work very well either. It still freezes the GUI with some really random UI updates that seem buggy at best.
The servers can handle 5-10 requests at a time, but these CSV files can be upwards of 5,000 lines sometimes.
So how can I limit the number of simultaneous PUT requests going out in my loop, while not having the GUI freeze on me? To be honest, I don't even really care if the end user can interact with the GUI while it's running, I just want to be able to provide feedback on the lines that have run so far.
I have a wrapper which a colleague wrote most of, and the async function looks like this:
func sendRequest(endpoint: String, method: HTTPMethod, base64credentials: String, dataType: DataType, body: Data?, queue: DispatchQueue, handler: #escaping (Response)->Swift.Void) {
let url = self.resourceURL.appendingPathComponent(endpoint)
var request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30.0)
request.httpMethod = "\(method)"
var headers = ["Authorization": "Basic \(base64credentials)"]
switch dataType {
case .json:
headers["Content-Type"] = "application/json"
headers["Accept"] = "application/json"
if let obj = body {
do {
request.httpBody = try JSONSerialization.data(withJSONObject: obj, options: JSONSerialization.WritingOptions(rawValue: 0))
} catch {
queue.async {
handler(.badRequest)
}
return
}
}
case .xml:
headers["Content-Type"] = "application/xml"
headers["Accept"] = "application/xml"
request.httpBody = body
/*if let obj = body {
request.httpBody = (obj as! XMLDocument).xmlData
}*/
}
request.allHTTPHeaderFields = headers
session.dataTask(with: request) {
var response: Response
if let error = $2 {
response = .error(error)
} else {
let httpResponse = $1 as! HTTPURLResponse
switch httpResponse.statusCode {
case 200..<299:
if let object = try? JSONSerialization.jsonObject(with: $0!, options: JSONSerialization.ReadingOptions(rawValue: 0)) {
response = .json(object)
} else if let object = try? XMLDocument(data: $0!, options: 0) {
response = .xml(object)
} else {
response = .success
}
default:
response = .httpCode(httpResponse.statusCode)
}
}
queue.async {
handler(response)
}
}.resume()
Then, there is a synchronous option which uses semaphore, which looks like this:
func sendRequestAndWait(endpoint: String, method: HTTPMethod, base64credentials: String, dataType: DataType, body: Data?) -> Response {
var response: Response!
let semephore = DispatchSemaphore(value: 0)
sendRequest(endpoint: endpoint, method: method, base64credentials: base64credentials, dataType: dataType, body: body, queue: DispatchQueue.global(qos: .default)) {
response = $0
semephore.signal()
}
semephore.wait()
return response
}
Usage information is as follows:
class ViewController: NSViewController {
let client = JSSClient(urlString: "https://my.mdm.server:8443/", allowUntrusted: true)
let credentials = JSSClient.Credentials(username: "admin", password: "ObviouslyNotReal")
func asynchronousRequestExample() {
print("Sending asynchronous request")
client.sendRequest(endpoint: "computers", method: .get, credentials: credentials, dataType: .xml, body: nil, queue: DispatchQueue.main) { (response) in
print("Response recieved")
switch response {
case .badRequest:
print("Bad request")
case .error(let error):
print("Receieved error:\n\(error)")
case .httpCode(let code):
print("Request failed with http status code \(code)")
case .json(let json):
print("Received JSON response:\n\(json)")
case .success:
print("Success with empty response")
case .xml(let xml):
print("Received XML response:\n\(xml.xmlString(withOptions: Int(XMLNode.Options.nodePrettyPrint.rawValue)))")
}
print("Completed")
}
print("Request sent")
}
func synchronousRequestExample() {
print("Sending synchronous request")
let response = client.sendRequestAndWait(endpoint: "computers", method: .get,credentials: credentials, dataType: .json, body: nil)
print("Response recieved")
switch response {
case .badRequest:
print("Bad request")
case .error(let error):
print("Receieved error:\n\(error)")
case .httpCode(let code):
print("Request failed with http status code \(code)")
case .json(let json):
print("Received JSON response:\n\(json)")
case .success:
print("Success with empty response")
case .xml(let xml):
print("Received XML response:\n\(xml.xmlString(withOptions: Int(XMLNode.Options.nodePrettyPrint.rawValue)))")
}
print("Completed")
}
override func viewDidAppear() {
super.viewDidAppear()
synchronousRequestExample()
asynchronousRequestExample()
}
I have modified the send functions slightly, so that they take base64 encoded credentials off the bat, and maybe one or two other things.
Can't you just chain operations to send 3/4 requests at a time per operation?
https://www.raywenderlich.com/76341/use-nsoperation-nsoperationqueue-swift
Just so you know, NSOperation (also abstracted by Operation with Swift3) are running by default on background threads. Just be careful to not run heavy tasks in your completion block that might run tasks on the main thread (this will freeze your UI).
The only other case I see that can freeze your UI is by executing too many operations at once.
Well, I think I got this covered! I decided to climb out of the rabbit hole a ways and simplify things. I wrote my own session instead of relying on the wrapper, and set up semaphores in it, threw it in an OperationQueue and it seems to be working perfectly.
This was the video I followed to set up my simplified semaphores request. https://www.youtube.com/watch?v=j4k8sN8WdaM
I'll have to tweak the below code to be a PUT instead of the GET I've been using for testing, but that part is easy.
//print (row[0])
let myOpQueue = OperationQueue()
myOpQueue.maxConcurrentOperationCount = 3
let semaphore = DispatchSemaphore(value: 0)
var i = 0
while i < 10 {
let myURL = NSURL(string: "https://my.server.com/APIResources/computers/id/\(i)")
myOpQueue.addOperation {
let request = NSMutableURLRequest(url: myURL! as URL)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = ["Authorization" : "Basic 123456789ABCDEFG=", "Content-Type" : "text/xml", "Accept" : "text/xml"]
let session = Foundation.URLSession(configuration: configuration)
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(data, response, error) -> Void in
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
semaphore.signal()
self.lblLine.stringValue = "\(i)"
self.appendLogString(stringToAppend: "\(httpResponse.statusCode)")
print(myURL!)
}
if error == nil {
print("No Errors")
print("")
} else {
print(error!)
}
})
task.resume()
semaphore.wait()
}
i += 1
}