Swift using Alamofire to make a http request - swift

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

Related

How to save data from JSON to model

I have an API, I parse it with alomofire, and I want to save parsed json to Model
This is my request and I want to save chats to ChatInstanceModel.
This is my NetworkingManager class
func getChatList(completionHandler: (#escaping (ChatInstanceModel) -> Void)) {
guard let url = URL(string: urlOfIntanceAPI) else { return }
let headers: HTTPHeaders = [
"Authorization" : "FastToken 8af34fc7c2557517d8a3e747e497de6491a16350df6fa0af8afcbbe72035484efae69d46efa8a47feb75be3250d025b01f42b3b5ad723e5a32afc887c1f6879629b86655ce49e19cada8e289b38d79061160f538c6fa59388076396de1cd2886e2c2e8c5f706ad2bb79a23f034b889c835dd288e44c0cc257d048a22093e51226a60b198bf72aa40a11829771147b4d5"
]
AF.request(url, method: .get, headers: headers).responseData { (responseData) in
switch responseData.result {
case .success(let data):
guard let chats = try? JSONDecoder().decode(ChatInstanceModel.self, from: data) else { return }
completionHandler(chats)
case .failure(let error):
print(error)
}
}
}
This is my model:
struct ChatInstanceModel: Codable {
let title: String
let avatar: String?
let unread_client: Int?
}
This is api which I want to save
{
"pk": 3,
"title": "None",
"avatar": null,
"unread_client": 0,
"unread_manager": 0,
"is_online": false,
"is_typing": false,
"last_seen": null,
"manager": null,
"last_update": null
}
Here is an example of how you can save the ChatInstanceModel instance after the getting the response from API using the completionHandler.
class ViewController: UIViewController {
var chats: ChatInstanceModel?
override func viewDidLoad() {
super.viewDidLoad()
getChatList { (chats) in
self.chats = chats
}
}
}
That's just a simple example for your problem statement currently specified.
Please find an example:
import Alamofire
// Your Model
struct ChatInstanceModel: Codable {
let title: String
let avatar: String?
let unread_client: Int?
}
// Your View Controller
class ViewController: UIViewController {
// Your chat instance
var chats: ChatInstanceModel? = nil
override func viewDidLoad() {
super.viewDidLoad()
getChatList { (chats) in
self.chats = chats
print(self.chats?.title)
}
}
// Get chat list from api
func getChatList(completionHandler: (#escaping (ChatInstanceModel) -> Void)) {
guard let url = URL(string: "<API_URL>") else { return }
let headers: HTTPHeaders = [
"Authorization" : "<TOKEN>"
]
let request = AF.request(url, method: .get, headers: headers)
request.responseDecodable(of: ChatInstanceModel.self) { (dataResponse) in
switch dataResponse.result {
case .success(let data):
completionHandler(data)
case .failure(let error):
print("Error", error.localizedDescription)
}
}
}
}

How can I write a generic wrapper for alamofire request in swift?

How can I write a generic wrapper for alamofire request ?
How can I convert the POST and GET function to Generic function in the following code?
I need to have generic request functions that show different behaviors depending on the type of data received.
Also, Can the response be generic?
My non-generic code is fully outlined below
import Foundation
import Alamofire
import SwiftyJSON
// for passing string body
extension String: ParameterEncoding {
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var request = try urlRequest.asURLRequest()
request.httpBody = data(using: .utf8, allowLossyConversion: false)
return request
}
}
public class ConnectionManager {
func Post (FirstName: String, LastName: String, NationalID: String, NationalCode: String, BirthDate: Date,Image: String,isFemale: Bool,Age:Int64,Avg:Double, completion: #escaping CompletionHandler) {
let body: [String:Any] = [
"FirstName":FirstName,
"LastName": LastName,
"NationalID": NationalID,
"NationalCode": NationalCode,
"BirthDate":BirthDate,
"Image": Image,
"isFemale": isFemale,
"Age": Age,
"Avg": Avg
]
Alamofire.request(BASE_URL, method: .post, parameters: body, encoding: JSONEncoding.default, headers: HEADER).responseJSON { (response) in
if response.response?.statusCode == 200 {
guard let data = response.result.value else { return }
print(data)
completion(true)
} else {
print("error reg auth service")
guard let er = response.result.value else { return }
print(er)
completion(false)
debugPrint(response.result.error as Any)
}
}
}
func get(FirstName: String, LastName: String, NationalID: String, NationalCode: String, BirthDate: Date,Image: String,isFemale: Bool,Age:Int64,Avg:Double, completion: #escaping CompletionHandler) -> [Person] {
let body: [String:Any] = [
"FirstName":FirstName,
"LastName": LastName,
"NationalID": NationalID,
"NationalCode": NationalCode,
"BirthDate":BirthDate,
"Image": Image,
"isFemale": isFemale,
"Age": Age,
"Avg": Avg
]
Alamofire.request(BASE_URL, method: .get, parameters: body, encoding: JSONEncoding.default, headers: HEADER).responseJSON { (response) in
if response.response?.statusCode == 200 {
print("no error login in authservice")
guard let data = response.result.value else { return }
print(data)
completion(true)
}
else if response.response?.statusCode == 400 {
print("error 400 login in authservice")
guard let er = response.result.value else { return }
print(er)
debugPrint(response.result.error as Any)
completion(false)
} else {
print("error ## login in authservice")
guard let er = response.result.value else { return }
print(er)
debugPrint(response.result.error as Any)
completion(false)
}
}
return [Person]()
}
}
The best idea is to use the URLRequestConvertible Alamofires protocol and create your own protocol and simple structs for every API request:
import Foundation
import Alamofire
protocol APIRouteable: URLRequestConvertible {
var baseURL: String { get }
var path: String { get }
var method: HTTPMethod { get }
var parameters: Parameters? { get }
var encoding: ParameterEncoding { get }
}
extension APIRouteable {
var baseURL: String { return URLs.baseURL }
// MARK: - URLRequestConvertible
func asURLRequest() throws -> URLRequest {
let url = try baseURL.asURL().appendingPathComponent(path)
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = method.rawValue
return try encoding.encode(urlRequest, with: parameters)
}
}
and request can look like this:
struct GetBooks: APIRouteable {
var path: String
var method: HTTPMethod
var parameters: Parameters?
var encoding: ParameterEncoding
}
and inside the APIClient prepare the generic method:
func perform<T: Decodable>(_ apiRoute: APIRouteable,
completion: #escaping (Result<T>) -> Void) {
let dataRequest = session
.request(apiRoute)
dataRequest
.validate(statusCode: 200..<300)
.responseDecodable(completionHandler: { [weak dataRequest] (response: DataResponse<T>) in
dataRequest.map { debugPrint($0) }
let responseData = response.data ?? Data()
let string = String(data: responseData, encoding: .utf8)
print("Repsonse string: \(string ?? "")")
switch response.result {
case .success(let response):
completion(.success(response))
case .failure(let error):
completion(.failure(error))
}
})
}
and use it:
func getBooks(completion: #escaping (Result<[Book]>) -> Void) {
let getBooksRoute = APIRoute.GetBooks()
perform(getBooksRoute, completion: completion)
}

Queue with alamofire

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)
}
}
}

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))")
}
}
}
}

How to get Freddy working with AlamoFire in Swift?

I am trying to authenticate my users using an Alamofire POST request. I get an response object as a dictionary. I wish I could get a Freddy LoginResponse object from that dictionary. Swift is new to me.
import UIKit
import Alamofire
import Freddy
public struct LoginResponse {
public let Response: String
public let Success: Int
public let Time: String
}
extension LoginResponse: JSONDecodable {
public init(json value: JSON) throws {
Response = try value.string("Response")
Success = try value.int("Success")
Time = try value.string("Time")
}
}
class LoginViewController: UIViewController {
#IBOutlet weak var emailOutlet: UITextField!
#IBOutlet weak var passwordOutlet: UITextField!
#IBAction func LoginAction(sender: AnyObject){
var parameters = [String: AnyObject]()
parameters["email"] = self.emailOutlet.text
parameters["password"] = self.passwordOutlet.text
Alamofire.request(.POST, "https://cool.api/iot/users/login", parameters: parameters, encoding: .JSON)
.responseJSON
{ response in switch response.result {
case .Success(let JSON):
print("Success with JSON: \(JSON)")
let response = JSON as! NSDictionary
print(response["Response"]!)
// What I am trying !!
// do {
// let response = JSON as! NSDictionary
// // Assuming `json` has the JSON data
// let attrs = try JSON.array("SUCCESS")
// let theLoginResponseObject = try attrs.map(LoginResponse.init)
// } catch {
//
// }
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.emailOutlet.text = "myemail#gmail.com"
self.passwordOutlet.text = "passwd"
}
}
The console outputs
Success with JSON: {
Response = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXZpY2VzIjpbImM5NGYyZjY0MDkwZmU4MWFmZjk5MGNkNTU0OTZhZjhkIiwiZjdmOGYzNWI0NDRiYmM3NzcxNzYxNjhlNTcxZjgzNjUiXSwiZW1haWwiOiJmcmFuY2VzY29hZmVycmFyb0BnbWFpbC5jb20iLCJleHAiOjE0NjU3MzcyOTUsImlkIjoiNTc0Y2E0NDkzMTcyODUwMDAxNjkzOGQ2Iiwicm9sZSI6Im1hc3RlciJ9.P-QxGCUTi1YWq46HJQlR2K-4S_DBKFxOLiyzqvE-r7S96XSxx02dpT8jOlZm4gx2qVrcj5wFyowJzy8HtU-y030I6OmftGe_dn2AgMJCD8dLXrRiRWfnWK5nhN6BvDJqCLyN_BopKGM2stEf7stavoPogy4HxBfg_hWIFJEwdHs";
Success = 200;
Time = "2016-06-12T13:04:55.426208276Z";
}
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJkZXZpY2VzIjpbImM5NGYyZjY0MDkwZmU4MWFmZjk5MGNkNTU0OTZhZjhkIiwiZjdmOGYzNWI0NDRiYmM3NzcxNzYxNjhlNTcxZjgzNjUiXSwiZW1haWwiOiJmcmFuY2VzY29hZmVycmFyb0BnbWFpbC5jb20iLCJleHAiOjE0NjU3MzcyOTUsImlkIjoiNTc0Y2E0NDkzMTcyODUwMDAxNjkzOGQ2Iiwicm9sZSI6Im1hc3RlciJ9.P-QxGCUTi1YWq46HJQlR2K-4S_DBKFxOLiyzqvE-r7S96XSxx02dpT8jOlZm4gx2qVrcj5wFyowJzy8HtU-y030I6OmftGe_dn2AgMJCD8dLXrRiRWfnWK5nhN6BvDJqCLyN_BopKGM2stEf7stavoPogy4HxBfg_hWIFJEwdHs
Error
/Users/cesco/code/iot/iot/LoginViewController.swift:50:37: Value of type 'NSDictionary' has no member 'array'
You can use the following GET call to retrieve JSON data to use with Freddy.
Alamofire.request(.GET, "http://api.openweathermap.org/data/2.5/weather?APPID=<123456>").responseJSON { (response) -> Void in
if response.result.isSuccess {
do {
let json = try JSON(data: response.data!)
let description = try json.array("weather")[0]["description"]
print(description)
} catch {
print("There is an error")
}
}
}
Or if you are using the latest AlamoFire as of August 2016:
Alamofire.request( "http://api.openweathermap.org/data/2.5/weather?APPID=<123456>", withMethod: .get).responseJSON { (response) -> Void in
if response.result.isSuccess {
do {
let json = try JSON(data: response.data!)
let description = try json.array("weather")[0]["description"]
print(description)
} catch {
print("There is an error")
}
}
}