Performing tasks sequentially in Swift [duplicate] - ios

This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed 4 years ago.
I'm trying to verify a login and return a boolean value from my function accordingly, but my return statement keeps getting executed before the web service function is completed, even if I use an async method. I am using both Alamofire and SwiftyJSON.
I'm attaching my code below. Any help would be appreciated!
Thanks.
func checkUs (name: String, password: String) -> Bool
{
bool authen = false
DispatchQueue.global(qos: .userInitiated).async {
let jsonDic : [String: String] = ["email": name, "pass": password]
Alamofire.request("enter URL here", method: .post, parameters: jsonDic, encoding: JSONEncoding.default, headers: nil).responseJSON { (response) in
switch(response.result) {
case .success(let sentJSON):
let gotJSON = JSON (sentJSON)
print (gotJSON[0]["status"].boolValue)
authen = gotJSON[0]["status"].boolValue
case .failure(let err):
print(err)
}
print ("First ", authen)
}
}
print ("Second", authen)
return authen
//return true
Log Output:
Second false
true
First true

You need completion , also Alamfire runs asynchronously no need for global queue
func checkUs (name: String, password: String,completion: #escaping (_ status: Bool,_ err:Error?) -> Void) {
bool authen = false
let jsonDic : [String: String] = ["email": name, "pass": password]
Alamofire.request("enter URL here", method: .post, parameters: jsonDic, encoding: JSONEncoding.default, headers: nil).responseJSON { (response) in
switch(response.result) {
case .success(let sentJSON):
let gotJSON = JSON (sentJSON)
print (gotJSON[0]["status"].boolValue)
authen = gotJSON[0]["status"].boolValue
completion(authen,nil)
case .failure(let err):
print(err)
completion(authen,err)
}
print ("First ", authen)
}
print ("Second", authen)
}
//
call it like this
self.checkUs(name: "endedName", password: "sendedPassword") { (status, error) in
if let err = error {
}
else
{
}
}

Related

How to create delegate/callback inside framework in Swift? [duplicate]

This question already has answers here:
How could I create a function with a completion handler in Swift?
(9 answers)
Closed 3 years ago.
I have been implementing login framework to use in app development. I have created login framework functionality and it works fine.
Now i'm trying to do callback of return response of whether it success or failed from app to framework wise versa framework to app.
here is my framework code:
public func loginApiCall(username: String?, password: String?) {
let parameters = [
"username": username,
"password": password
]
print(parameters)
let url = "apiUrl/authentication/"
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: [:]).responseJSON {
response in
switch (response.result) {
case .success:
print(response)
break
case .failure:
print(Error.self)
}
}
}
Here is my sampleapp where i have using myframework:
override func viewDidLoad() {
super.viewDidLoad()
//framework called here...
let apiCall = APICall.init()
apiCall.loginApiCall(username: "demo", password: "demo")
}//viewdidload
you can achieve using callback
// framework code
public func loginApiCall(username: String?, password: String?, callback : #escaping ((Bool) -> Void)) {
let parameters = [
"username": username,
"password": password
]
print(parameters)
let url = "apiUrl/authentication/"
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: [:]).responseJSON {
response in
switch (response.result) {
case .success:
callback(true)
case .failure:
callback(false)
}
}
}
// viewDidLoad code
override func viewDidLoad() {
super.viewDidLoad()
//framework called here...
let apiCall = APICall.init()
apiCall.loginApiCall(username: "demo", password: "demo") { (status) in
print("code")
}
}
//NOTE : create completionHandler as per your requirement, it is a demo completionHandler
first you have to define completion handler, after then you can use it inside of your function parameter. I suggest you to check out completions to achieve that easily.
typealias DownloadComplete = () -> ()
public func loginApiCall(username: String?, password: String?, completed: #escaping DownloadComplete))
{
let parameters = [
"username": username,
"password": password
]
print(parameters)
let url = "apiUrl/authentication/"
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: [:]).responseJSON {
response in
switch (response.result) {
case .success:
completed(). // important part
case .failure:
}
}

Alamofire generalize request

I am using Almofire for all my requests and works fine. I need to know how to generalize all requests to handle all errors at one place.
func updateSettingValue(group : String , value: String , callback: #escaping (SettingsResponseModel) -> Void, errorCallback: #escaping (Error) ->Void)
{
let url = BASE_URL_PROD + API_SETTINGS
let settingsParams : Parameters = ["Setting" : group , "Tag" : value]
Alamofire.request(url, method: .put, parameters: settingsParams, headers: getHeader()).responseObject {
(response: DataResponse< SettingsResponseModel>) in
switch response.result {
case .success:
print("response \(response)")
DispatchQueue.main.async {
callback(response.result.value!)
}
break
case .failure(let error):
print(error)
errorCallback(error)
}
}
}
func releaseKeys(mKey: String ,callback: #escaping (ReleaseKeyModel) -> Void
, errorCallback: #escaping (Error) -> Void){
let url = BASE_URL_PROD + API_RELEASE_KEY
let params: Parameters = ["mKey" : mKey]
Alamofire.request(url, method: .delete, parameters: params, encoding: URLEncoding.default, headers: getHeader()).responseObject{
(response : DataResponse< ReleaseKeyModel >) in
print("releaseKey: \(response) ")
switch response.result {
case .success:
DispatchQueue.main.async {
callback(response.result.value!)
}
break
case .failure(let error):
print(error)
errorCallback(error)
}
}
}
How can I generalize this to take parameters for Mapping Model class in DataResponse so that I don't have to handle success and failure case individually for all methods.
You can divide the work with the server into 2 classes:
1) class RestClient
import Foundation
typealias responseBlock = (_ swiftObj: Any?, _ error: Error?) -> Void
class RestClient: NSObject {
static let shared = RestClient()
private var http = HttpService()
func updateSettingValue(group: String, value: String, resp: #escaping responseBlock) {
let url = BASE_URL_PROD + API_SETTINGS
let params = ["Setting": group, "Tag": value]
http.reque(url, method: .put, parameters: params, headers: getHeader(), resp: { (value, error) in
if let err = error {
return resp(nil, err)
}
guard let data = value else {
return resp(nil, error)
}
//your method for parse data
self.parseData(respData: data,
modelCls: SettingsResponseModel.self,
response: resp)
})
}
func releaseKeys(mKey: String, resp: #escaping responseBlock) {
let url = BASE_URL_PROD + API_RELEASE_KEY
let params = ["mKey": mKey]
http.reque(url, method: .delete, parameters: params, encoding: URLEncoding.default, headers: getHeader(), resp: { (value, error) in
if let err = error {
return resp(nil, err)
}
guard let data = value else {
return resp(nil, error)
}
//your method for parse data
self.parseData(respData: data,
modelCls: ReleaseKeyModel.self,
response: resp)
})
}
}
2) class HttpService
class HttpService {
func reque(_ url: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: HTTPHeaders? = nil,
queue: QueueQos = .defaultQos,
resp: #escaping responseBlock) {
Alamofire.request(url,
method: method,
parameters: parameters,
encoding: encoding,
headers: headers
).responseObject (queue: queue) { (response) in
switch response.result {
case .success:
if let jsonResp = response.result.value {
//You can also check out some error messages at this place.
resp(jsonResp, nil)
}
case .failure(let error):
resp(nil, error)
}
}
}
}
Try this, using generics
func releaseKeys<T: Codable>(parameters params: [String: Any], callback: #escaping (T) -> Void
, errorCallback: #escaping (Error) -> Void){
Alamofire.request(url, method: .delete, parameters: params, encoding: URLEncoding.default, headers: getHeader()).responseObject{
(response : DataResponse< T >) in
print("releaseKey: \(response) ")
switch response.result {
case .success:
DispatchQueue.main.async {
callback(response.result.value!)
}
break
case .failure(let error):
print(error)
errorCallback(error)
}
}
}

Issue with parsing json using Alamofire

My JSON response in postman looks like this...
{
"success": 1,
"Details": {
"seller_id": "165",
"mobile_no": "9653265987",
"seller_name": "User A",
"seller_email_id": "user#gmail.com",
"company_name": "myCompany",
"category": "Cosmetics",
"otp": "1111"
},
"message": "Seller Already Registered!!!"
}
Now I wanted to give a condition based on whether success is 0 or 1 and so for that I wanted to extract success. Also I want to extract mobile number. But I am not able figure out how to do that.
This is how I am making my Alamofire post request...
Alamofire.request(url, method: .post, parameters: Parameters, encoding: URLEncoding.httpBody, headers: headers)
.responseString { (response) in
if let httpResponse = response.response {
print("error \(httpResponse.statusCode)")
if httpResponse.statusCode == 200 {
//Do something
}
}
}
I did go through similar issues raised by other users...but couldn't find from them an exact solution for my issue...
Try this
Alamofire.request(url, method: .post, parameters: Parameters, encoding: URLEncoding.httpBody, headers: headers)
.responseJSON { (response) in
switch response.result{
case .success(_):
print("Success: \(response)")
let JsonResponse = response.result.value as? [String: Any]
if JsonResponse?["success"] as! Int == 1{
let dict = JsonResponse?["Details"] as! [String : Any]
let mobileNo = dict["mobile_no"] as! String
}
else{
print("Failed")
}
case .failure(let error):
print("Failed: \(error)")
}
}
There are a couple of issues:
Your parameters to request don't look quite right.
the parameters parameter doesn't look right: at best, it's very strange to have a Parameters variable (you should use lower case for variables; and this conflicts with an Alamofire type);
the encoding should be URLEncoding.default. Or, because URLEncoding.default is the default value, you can omit this altogether.
If you want to check for status code, let validate do that for you.
If you're expecting JSON in response, use reponseJSON instead of responseString.
Use if let or guard let with as? to safely unwrap optionals. Do not use as! (unless you are 100% sure that it never could fail ... and when dealing with responses from a remote server, you can never be 100% confident).
Thus:
func performRequest(url: String, parameters: [String: String]?, headers: [String: String]?) {
Alamofire.request(url, method: .post, parameters: parameters, headers: headers)
.validate(statusCode: 200 ..< 300)
.responseJSON { response in
switch response.result {
case .success(let value):
guard let dictionary = value as? [String: Any],
let success = dictionary["success"] as? Int else {
print("success not found")
return
}
guard success == 1 else {
print("web service reported failure")
return
}
guard let details = dictionary["Details"] as? [String: Any] else {
print("Did not find details")
return
}
guard let mobile = details["mobile_no"] as? String else {
print("mobile not found")
return
}
print(mobile)
case .failure(let error):
print("Failed: \(error)")
}
}
}
Or, in your comment below, if you want to goToSignUpScreen() if success was 0, then:
Alamofire.request(url, method: .post, parameters: parameters, headers: headers)
.validate(statusCode: 200 ..< 300)
.responseJSON { response in
switch response.result {
case .success(let value):
guard let dictionary = value as? [String: Any],
let success = dictionary["success"] as? Int else {
print("success not found")
return
}
if success == 0 {
self.goToSignUpScreen()
}
case .failure(let error):
print("Failed: \(error)")
}
}

Alamofire completion handler not being called

I'm currently trying to call a prepareForSegue method in an AlamoFire completion handler but it's not being called. Here is my code:
func loginMember (username: String, password: String, completionHandler: (String?, ErrorType?) -> ()) {
let headers = [
"Cache-Control": "no-cache",
"Content-Type": "application/json"
]
let parameters: [String: AnyObject] = [
"grant_type" : "password",
"username" : username,
"password" : password,
]
Alamofire.request(.POST, "\(baseURL)/oauth2/token", parameters: parameters, encoding: .JSON, headers: headers)
.validate()
.responseJSON { response in
switch response.result {
case .Success:
guard let value = response.result.value else {
completionHandler(nil, response.result.error)
return
}
let swiftyJsonVar = JSON(value)
accessToken = swiftyJsonVar["access_token"].stringValue
print("This is the login response:\(swiftyJsonVar)")
case .Failure(let error):
print("Sorry there was an error: \(error)")
return
}
}
}
This is what it looks like when called:
loginMember(username, password: password, completionHandler: { error in
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("loginToHomeSegue", sender: self)
}
}
)
Any ideas as to why the performSegueWithIdentifier isn't being called?
You're only calling your completion handler in the case where you enter your guard statement. You need to add calls for the case where you get your access token and your error case.
Alamofire.request(.POST, "\(baseURL)/oauth2/token", parameters: parameters, encoding: .JSON, headers: headers)
.validate()
.responseJSON { response in
switch response.result {
case .Success:
guard let value = response.result.value else {
completionHandler(nil, response.result.error)
return
}
let swiftyJsonVar = JSON(value)
accessToken = swiftyJsonVar["access_token"].stringValue
print("This is the login response:\(swiftyJsonVar)")
// Got the token, call handler
completonHandler(accessToken, nil)
case .Failure(let error):
print("Sorry there was an error: \(error)")
// Got an error, call handler
completionHandler(nil, error)
return
}
}

wait for response Alamofire swift

I need to wait for response.response?.allHeaderFields data before executing function. I've searched the net and didn't quite get how to add "completion handler" to alamofire request. Or if there are any other ways to make the function wait.
#IBAction func comfirmation(sender: UIButton) {
if CodeTextField.text != "" {
print("in comfirmation function")
let comfirmationRequestData = [
"phone" : "\(appDelegate.savedNumber)",
"code" : "\(CodeTextField.text!)"
]
Alamofire.request(.POST,
"http://192.168.214.241:4000/login",
parameters: comfirmationRequestData,
encoding: .JSON).responseJSON {
response in
switch response.result {
case .Success:
let jsonDecrypted = JSON(response.result.value!)
print(jsonDecrypted)
let headerFile = response.response?.allHeaderFields as? [String:String]
print(headerFile)
case .Failure(let error):
print(error)
}
}
print("in comfirmation function. success")
appDelegate.defaults.setValue(appDelegate.savedNumber, forKey: "phoneNumber")
} else {
print("in comfirmation function. failed")
}
}
Use Alamofire like this
func postRequest( urlSuffix : String, params:[String : AnyObject]?, filterParams : [String]?, success: (response: AnyObject!) -> Void, failure: (error: NSError?) -> Void)
{
Alamofire.request(.POST, webServicesURLPrefix + urlSuffix, parameters: params, encoding: .JSON, headers: self.headers)
request?.responseJSON { response in
switch response.result
{
case .Success:
success(response: response.result.value)
case .Failure(let error):
failure(error: error)
}
}
}
Call the method from anywhere as
self.postRequest("do-registration.php", params: params, filterParams: nil, success: { (response) -> Void in
self.afterResponse(response)
}) { (error) -> Void in
failure(error: error)
}
OR you can write a saperate method which you will have to call after the completion.
func afterResponse(responseData : AnyObject)
{
print("Done")
print(responseData)
}
You can cause the operation to be synchronous, but in order to do that you are going to have to use a semaphore for that you set up prior to the Alamofire request, and that you then release within the completion handler. You will wait on the semaphore right after you initiate Alamo fire.
There is a library Alamofire-Synchronous which works using semaphore.
Sample:
//json
let response = Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON()
if let json = response.result.value {
print(json)
}

Resources