Queue with alamofire - swift

I have a problem with the execution of the tasks when i use Alamofire
I use two time Alamofire, a first to collect a data (token) that I will then use it to send my Post request.
The problem between my two requests, the recovery of the data is done after the second request.
import Foundation
import Alamofire
import SwiftyJSON
class Helper {
func alomofireGet(URL: String) -> JSON {
let queue = DispatchQueue(label: "com.test.com", qos: .background, attributes: .concurrent)
var contenuJSON = JSON()
Alamofire.request(URL, method: .get).responseJSON(queue: queue) { (reponse) in
if reponse.result.isSuccess {
contenuJSON = JSON(reponse.result.value!)
print(contenuJSON)
}
else {
contenuJSON = JSON(reponse.result.error!)
}
}
return contenuJSON
}
func alomofirePost(URL: String, Paramaters: Dictionary<String, Any>) -> JSON {
var contenuJSON = JSON()
Alamofire.request(URL, method: .post, parameters: Paramaters, encoding: JSONEncoding.default).responseJSON { (reponse) in
if reponse.result.isSuccess {
contenuJSON = JSON(reponse.result.value!)
}
else {
contenuJSON = JSON(reponse.result.error!)
}
}
return contenuJSON
}
}
In the new file = DIFFERENCE WITH CONTENT TOKEN
let request = Helper()
#IBOutlet weak var emailText: UITextField!
#IBOutlet weak var passwordText: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
self.hideKeyboardWhenTappedAround()
}
#IBAction func login(_ sender: Any) {
let contenuJSON = request.alomofireGet(URL: "http://192.168.1.7/app_dev.php/login/app")
print(contenuJSON)
let token = contenuJSON["csrfToken"].stringValue
print(token) // /\ EMPTY
let Paramaters = ["_csrf_token": token, "_password": self.passwordText.text!, "_redirect_url": "", "t_path": "", "_username": self.emailText.text!]
let contenuRequest = request.alomofirePost(URL: "http://192.168.1.7/app_dev.php/login_check", Paramaters: Paramaters)
print(token) // /\ FULL /\
}
}

API call to Alamofire are the async process, hence your alamofireGet and alamofirePost returning just initialized json object - JSON() which does not have any data.
Solution:
You should use #escaping closure, which will hold the control until you get the result from first API call.
func alomofireGet(URL: String, onCompletion:((JSON) -> Void)) {
let queue = DispatchQueue(label: "com.test.com", qos: .background, attributes: .concurrent)
var contentJSON = JSON()
Alamofire.request(URL, method: .get).responseJSON(queue: queue) { (reponse) in
// Load contentJSON with data
if reponse.result.isSuccess {
contenuJSON = JSON(reponse.result.value!)
} else {
contenuJSON = JSON(reponse.result.error!)
}
// Send contentJSON via `onCompletion` block
onCompletion(contenuJSON)
}
}
func alomofirePost(URL: String, Paramaters: Dictionary<String, Any>, onCompletion: #escaping ((_ response: JSON) -> Void)) {
var contenuJSON = JSON()
Alamofire.request(URL, method: .post, parameters: Paramaters, encoding: JSONEncoding.default).responseJSON { (reponse) in
// Load contentJSON with data
if reponse.result.isSuccess {
contenuJSON = JSON(reponse.result.value!)
} else {
contenuJSON = JSON(reponse.result.error!)
}
// Send contentJSON via `onCompletion` block
onCompletion(contenuJSON)
}
}
Call it in your view-controller as:
let usernameStr = self.emailText.text!
let passwordStr = self.passwordText.text!
Helper().alomofireGet(URL: "http://192.168.1.7/app_dev.php/login/app") { contenuJSON in
print(contenuJSON)
DispatchQueue.main.async {
let token = contenuJSON["csrfToken"].stringValue
print(token)
let Paramaters = ["_csrf_token": token, "_password": passwordStr, "_redirect_url": "", "t_path": "", "_username": usernameStr]
Helper().alomofirePost(URL: "http://192.168.1.7/app_dev.php/login_check", Paramaters: Paramaters) { contenuJSON in
print(token)
}
}
}

Related

To cancle any api request Using Alamofire in swift

I am calling api using alamofire in swift.it is a search api, when I am searching any text in searchbar I am calling the api. I am calling the api after a delay of 0.75 seconds. I want to cancel the previous request, when new request is there. But I don’t know how to cancel the request. Can anyone help me?
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(self.reload(_:)), object: searchBar)
perform(#selector(self.reload(_:)), with: searchBar, afterDelay: 0.75)
}
#objc func reload(_ searchBar: UISearchBar) {
guard let query = searchBar.text, query.trimmingCharacters(in: .whitespaces) != "" else {
self.searchLcodeWithText("")
print("nothing to search")
return
}
self.searchLcodeWithText(query)
print(query)
}
private func searchLcodeWithText(_ newText: String){
//Show
startActivityIndicator()
apiService.searchlcode(searchText: newText) { [self] (lcodeData) in
allLcodeArray = []
//Hide
if let lcde = lcodeData{
if lcde.count > 0{
allLcodeArray.append(contentsOf: lcde)
stopActivityIndicator()
}
}
else{
}
stopActivityIndicator()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
import Foundation
import Alamofire
class ApiService{
func searchlcode(searchText: String, completion: #escaping searchLcodeTypeAliasCompletion){
guard let urlrequest = URL(string: SEARCH_URL) else {return}
var parameters = [String:Any]()
parameters.updateValue(searchText, forKey: "lcode_name")
Alamofire.request(urlrequest, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: nil).responseJSON { [self] (response : DataResponse<Any>) in
if let error = response.result.error {
debugPrint(error.localizedDescription)
return
}else{
guard let data = response.data else { return completion(nil)}
let jsonDecoder = JSONDecoder()
do {
let searchUser = try jsonDecoder.decode(searchLcodeTypeAlias.self, from: data)
print(searchUser)
completion(searchUser)
} catch {
debugPrint(error)
completion(nil)
}
}
}
}
}
Return DataRequest in function then you can cancel specific request.
for Example.
#discardableResult
func login(params: loginRequestModel, completion: #escaping (RequestResult<UserDataResponse, String>) -> Void) -> DataRequest {
params.printJson()
let url = baseUrl + endPoints.login.rawValue
let request = sessionManager.request(url,method: .post, parameters: params, headers: headers)
request.responseDecodable(of: UserDataResponse.self){ (response) in
completion(self.getValidResponse(response))
}
return request
}
usage in viewController
step1 declaration: var loginDataRequest: DataRequest?
step2: initialise data request in function call
step3: action event cancel this request
loginDataRequest.cancel
another approach
you can achieve by using dispatch work Item or operation Queue

Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread in swift

I have given DispatchQueue.main.async {} where it necessary but when i give break point from dataTask here it says
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'
Cannot be called with asCopy = NO on non-main thread.
class EventsViewController: UIViewController {
#IBOutlet weak var backBtn: UIButton!
var eventsListArray = [AnyObject]()
var eventType: String?
var eventList : EventsModel? = nil
#IBOutlet weak var eventsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getAllEventsList()
}
func getAllEventsList() {
DispatchQueue.main.async {
let deviceId: String = (UIDevice.current.identifierForVendor?.uuidString)!
let personalId: String = UserDefaults.standard.string(forKey: "regUserID") ?? ""//KeychainWrapper.standard.string(forKey: "USERID") ?? ""
let headers = ["deviceid": deviceId,"userType": "personal","key": personalId]
DispatchQueue.main.async {
let string = Constants.GLOBAL_URL + "/get/allevents"
var urlComponents = URLComponents(string: string)
let eventStatus = self.eventType
let requestEventType = URLQueryItem(name: "eventstatus", value: eventStatus)
urlComponents?.queryItems = [requestEventType]
let urlStr = urlComponents?.url
let request = NSMutableURLRequest(url: urlStr!, cachePolicy: .useProtocolCachePolicy,timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers as! [String : String]
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if error == nil {
let httpResponse = response as? HTTPURLResponse
if httpResponse!.statusCode == 200 {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! [String :AnyObject]
print("publish event \(jsonObject)")
self.eventList = EventsModel.init(fromDictionary: jsonObject)
DispatchQueue.main.async {
if self.eventList?.events.count != 0 {
DispatchQueue.main.async {
self.eventsTableView.reloadData()
}
}
else {
DispatchQueue.main.async {
Constants.showAlertView(alertViewTitle: "", Message: "No Events \(self.eventType)", on: self)
self.eventList?.events.removeAll()
self.eventsTableView.reloadData()
}
}
}
} catch { print(error.localizedDescription) }
} else {
Constants.showAlertView(alertViewTitle: "", Message: "Something went wrong, Please try again", on: self)
}
}
})
dataTask.resume()
}
}
}
}
You've probably missed a few spots where you're trying to present an alert when errors are thrown. Why don't you just enter the main queue right after the data request is complete.
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
DispatchQueue.main.async {
if error == nil {
//...
}
}
})

DispatchGroup with async task

enter code hereI try lot of thing with the dispatch group but I can't obtain stable result.
I use Alamofire for get data since my server. I've write a function in the Helper Class and I use this function in AppDelegate.swift.
I don't know if I put the dispatch group when I call the function so in AppDelegate or I put the dispatch group only in the function in Helper Class.
func alomofireGet(URL: String, onCompletion:#escaping ((JSON) -> Void)) {
// let group = DispatchGroup()
var contentJSON = JSON()
// group.enter()
Alamofire.request(URL, method: .get).responseJSON() { (reponse) in
if reponse.result.isSuccess {
contentJSON = JSON(reponse.result.value!)
} else {
contentJSON = JSON(reponse.result.error!)
}
// group.leave()
}
// group.notify(queue: .main) {
onCompletion(contentJSON)
}
In App delegate, I write a function who call the function in my class.
func connect() {
let group = DispatchGroup()
let _: Bool = KeychainWrapper.standard.removeObject(forKey: "token")
var token = String()
group.enter()
Helper().alomofireGet(URL: "http://192.168.1.19/app_dev.php/login/app") { contenuJSON in
token = contenuJSON["csrfToken"].stringValue
group.leave()
}
group.notify(queue: .main) {
let _: Bool = KeychainWrapper.standard.set(token, forKey: "token")
let t: String? = KeychainWrapper.standard.string(forKey: "token")
print(t!)
}
}
The problem is the variable "t" is empty.
And when I call the keychainWrapper in app delegate, the keychain is empty also.
PS : I have other task, I've just reduce my code
func alomofireGet(URL: String, onCompletion:#escaping ((JSON) -> Void)) {
// let group = DispatchGroup()
var contentJSON = JSON()
// group.enter()
Alamofire.request(URL, method: .get).responseJSON() { (reponse) in
if reponse.result.isSuccess {
contentJSON = JSON(reponse.result.value!)
} else {
contentJSON = JSON(reponse.result.error!)
}
// group.leave()
}
// group.notify(queue: .main) {// where you call wait()function. This blocks the current thread until the group’s tasks have completed.
onCompletion(contentJSON)
}
I try this but is not the solution. I've remove the function in my Helper. I'have juste this function in the app delegate.
func connect(onCompletion : #escaping (String) -> ()) {
let group = DispatchGroup()
var token = String()
let _: Bool = KeychainWrapper.standard.removeObject(forKey: "token")
group.enter()
Alamofire.request("http://192.168.1.19/app_dev.php/login/app", method: .get).responseJSON() { (reponse) in
if reponse.result.isSuccess {
let contentJSON = JSON(reponse.result.value!)
token = contentJSON["csrfToken"].stringValue
} else {
token = "Invalid Token"
}
group.leave()
}
group.notify(queue : DispatchQueue.global()) {
onCompletion(token)
}
}
when I print token, I have a empty message.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Thread.sleep(forTimeInterval: 1.5)
connect() { token in
print(token)
}
return true
}

unable to load JSON data in 'data' array

when I call this function the array 'data' is showing empty square brackets, it is not giving me any errors though
here is the code :-
import Foundation
import Alamofire
import SwiftyJSON
class Networking {
var data = [Item]()
let tVC = TableViewController()
let url = "https://api.coinmarketcap.com/v1/ticker/"
func getCoinData(url: String) {
Alamofire.request(url, method: .get)
.responseJSON { response in
if response.result.isSuccess {
let coinJSON : JSON = JSON(response.result.value!)
for i in 0..<coinJSON.count{
let coinName = Item(bitJSON: coinJSON[i])
self.data.append(coinName)
self.tVC.tableView.reloadData()
}
}
else {
print("Error: \(String(describing: response.result.error))")
}
}
}
}
Try this one.
Change your url, method is "Get" and your mapping model.
public static func getArrayInformation(completionHandler: #escaping (APIResponse) -> Void) {
let url = "your url"
let params: Parameters = ["username": "admin",
"password": "1234"]
Alamofire.request(url,
method: .post,
parameters: params,
encoding: JSONEncoding.default,
headers: ["Content-Type": "application/json"])
.responseDecodableObject(keyPath: nil, decoder: JSONDecoder(), completionHandler: { (handler: DataResponse<[Your Object Array]>) in
completionHandler(handler.result.value ?? [Your Object Array]())
})
}
why you reload Table every loop iteration , and instead Loop use Map
class Networking {
var data = [Item]()
let tVC = TableViewController()
let url = "https://api.coinmarketcap.com/v1/ticker/"
func getCoinData(url: String) {
Alamofire.request(url, method: .get)
.responseJSON { response in
if response.result.isSuccess {
let coinJSON : JSON = JSON(response.result.value!)
data = coinJSON.map({ (coinJson) -> Item in
return Item(bitJSON: coinJson)
})
DispatchQueue.main.async {
self.tVC.tableView.reloadData()
}
}
else {
print("Error: \(String(describing: response.result.error))")
}
}
}
}

Swift using Alamofire to make a http request

I got a problem in using Alamofire recently.
Here is my code
LoginViewController.swift
class LoginViewController: UIViewController {
#IBOutlet weak var name: UITextField!
#IBOutlet weak var email: UITextField!
#IBOutlet weak var password: UITextField!
let baseApi = BaseApi()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func loginButton(_ sender: UIButton) {
let dict = ["name":name.text,"email":email.text,
"password":password.text]
print("api succeed1")
let result = baseApi.login(paras: dict as! [String : String])
print("api succeed2")
if result[0]["status"].string == "success" {
print("api succeed3")
present( UIStoryboard(name: "Main", bundle:
nil).instantiateViewController
(withIdentifier:"TabBarController")
as! TabBarController, animated: true, completion: nil)
}
}
BaseApi.swift
class BaseApi{
func login(paras : [String:String]) -> JSON {
let url = URL(string: "http://127.0.0.1:8000/api/login")
let result = baseApi(url: url!,paras: paras)
print("BaseApi3333")
return result
}
func baseApi(url : URL,paras : [String:String]) -> JSON {
var json:JSON = []
let toke = getToken()
let parameters: Parameters = [
"name": paras["name"]!,
"email": paras["email"]!,
"password": paras["password"]!
]
let headers: HTTPHeaders = [
"Authorization": "Basic "+toke,
"Accept": "application/json"
]
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default).responseJSON { response in
switch response.result {
case .success(let value):
json = JSON(value)
print("baseAp2222")
print(json)
case .failure(let error):
print(error)
}
}
print("baseApi111")
print(json)
return json
}
}
Here is the log
api succeed1
baseApi111
[
]
BaseApi3333
api succeed2
baseAp2222
{
"status_code" : 200,
"status" : "success",
"data" : {
"token" : "xxxx"
}
}
My question is why print(baseApi111) come out before print("baseAp2222"),I need to return json,but looks like the excute orders are not right,so the return json is nil,how should I solve this problem?
You need to use callback closures to make a return call , you can not return data like this from api calls .
Let me give you an example - following method is making call to api using almofire -
func fetchDataFromWebService<T: Mappable>(_ parameters: Dictionary<String , AnyObject>, closure:#escaping (_ response: T) -> Void){
let url = getWebServiceUrl()
// let url = NSURL(string: getWebServiceUrl)
print("parameters = \(parameters)")
Alamofire.request(url, method: .get, parameters: parameters, headers: nil).responseJSON { (response:DataResponse<Any>) in
switch(response.result) {
case .success(_):
if response.response?.statusCode == 200 || response.response?.statusCode == 201 {
// print(response.result.value)
var user = Mapper<T>().map(JSONObject: response.result.value)
// var user = Mapper<T>().map(response.result.value)
if self.processSingleRecord() == true {
user = Mapper<T>().map(JSONObject: (response.result.value as! NSArray).object(at: 0))
// user = Mapper<T>().map(response.result.value?.objectAtIndex(0))
}
closure(user!)
// print("user = ",user)
}
else if response.response?.statusCode == 0{
// print(self.DisplayNetworkAvailabilityMessage())
}
else {
if let _ = response.result.value as? Error {
}
}
break
case .failure(let error):
debugPrint("getEvents error: \(error)")
SVProgressHUD.dismiss()
break
}
}
}
Now here is call to this method -
let anotherWebServiceHandler = DeviceTokenDataHandler.init() anotherWebServiceHandler.fetchDataFromWebService(["":""], closure: { (response:SignUpResponse) -> Void in
})
You need to understand sequential code execution - and Closures