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")
}
}
}
Related
I'm coming from a javascript background and am finding it difficult to understand how to store the response from a simple GET request in SWIFT.
I have an empty array named plants declared in my View Controller. The response from my GET request returns an array of plant names (strings). How can I assign the response array to the array plants?
The setup of my code looks like this:
class MyPlantsViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var addPlantTextField: UITextField!
var plants: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.tableFooterView = UIView(frame: CGRect.zero)
getAllPlants()
}
func getAllPlants() {
// Create URL
let url = URL(string: ".....com/api/plants")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print("error: \(error)")
} else {
if let response = response as? HTTPURLResponse {
print("statusCode: \(response.statusCode)")
}
if let data = data {
<<..... I have tried lots of things here......>>
}
}
}
task.resume()
}
......
You can use JSONDecoder to decode list of string as below,
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print("error: \(error)")
} else {
do {
self.plants = try JSONDecoder().decode([String].self, from: data!)
} catch {
print(error)
}
}
}
I am having trouble populating a UITextField with my returnHTML data that I get from my web-service.
If I have my web-service such that:
import Foundation;
class WebSessionCredentials {
static let requestURL = URL(string:"xxxx.on.ca/getData.aspx?requestType=Tech")!
var htmlbody: String?
var instancedTask: URLSessionDataTask?
static var sharedInstance = WebSessionCredentials()
init() {
self.instancedTask = URLSession.shared.dataTask(with: WebSessionCredentials.requestURL) { [weak self] (data,response,error) in
if let error = error {
// Error
print("Client Error: \(error.localizedDescription)")
return
}
guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
print("Server Error!")
return
}
guard let mime = response.mimeType, mime == "text/html" else {
print("Wrong mime type!");
return
}
if let htmlData = data, let htmlBodyString = String(data: htmlData, encoding: .utf8) {
self?.htmlbody = htmlBodyString;
};
};
};
};
Through this I should be able to access the returned HTML response through WebSessionCredentials.sharedInstance.htmlbody;
Verifying this in playground I seem to be getting the correct response within the class but when calling htmlbody from outside the class I get a nil response - I am out of ideas in terms of how to send that HTML string that I get from the class to outside the function. This question is built off another question I have posted a couple days earlier -> Delegating privately declared variables to a public scope
Thanks,
Rather than implementing the dataTask in the init method add a method run with completion handler
class WebSessionCredentials {
enum WebSessionError : Error {
case badResponse(String)
}
static let requestURL = URL(string:"xxxx.on.ca/getData.aspx?requestType=Tech")!
static var sharedInstance = WebSessionCredentials()
func run(completion : #escaping (Result<String,Error>) -> Void) {
let instancedTask = URLSession.shared.dataTask(with: WebSessionCredentials.requestURL) { (data,response,error) in
if let error = error {
// Error
print("Client Error: \(error.localizedDescription)")
completion(.failure(error))
return
}
guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else {
completion(.failure(WebSessionError.badResponse("Server Error!")))
return
}
guard let mime = response.mimeType, mime == "text/html" else {
completion(.failure(WebSessionError.badResponse("Wrong mime type!")))
return
}
completion(.success(String(data: data!, encoding: .utf8)!))
}
instancedTask.resume()
}
}
And use it
WebSessionCredentials.sharedInstance.run { result in
switch result {
case .success(let htmlBody): print(htmlBody)
case .failure(let error): print(error)
}
}
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
I have taken over a Swift project and need to add Facebook login functionality. I am getting it to mostly work but am having a problem with this sample code here (https://developers.facebook.com/docs/swift/graph):
import FacebookCore
struct MyProfileRequest: GraphRequestProtocol {
struct Response: GraphResponseProtocol {
init(rawResponse: Any?) {
// Decode JSON from rawResponse into other properties here.
}
}
var graphPath = "/me"
var parameters: [String : Any]? = ["fields": "id, name"]
var accessToken = AccessToken.current
var httpMethod: GraphRequestHTTPMethod = .GET
var apiVersion: GraphAPIVersion = .defaultVersion
}
let connection = GraphRequestConnection()
connection.add(MyProfileRequest()) { response, result in
switch result {
case .success(let response):
print("Custom Graph Request Succeeded: \(response)")
print("My facebook id is \(response.dictionaryValue?["id"])")
print("My name is \(response.dictionaryValue?["name"])")
case .failed(let error):
print("Custom Graph Request Failed: \(error)")
}
}
connection.start()
I'm getting an error on compiling the for the line with the dictionaryValue optional saying /Users/jt/a-dev/tabfb/tabfb/LoginViewController.swift:72:31: Value of type 'MyProfileRequest.Response' has no member 'dictionaryValue' . How would I access the user name or id using this?
I faced this problem today as well. I got the user id and name inside MyProfileRequest
struct Response: GraphResponseProtocol {
init(rawResponse: Any?) {
// Decode JSON from rawResponse into other properties here.
guard let response = rawResponse as? Dictionary<String, Any> else {
return
}
if let name = response["name"],
let id = response["id"] {
print(name)
print(id)
}
}
}
EDIT: I redesigned my code like this to use the values in .success(let response) case
struct Response: GraphResponseProtocol {
var name: String?
var id: String?
var gender: String?
var email: String?
var profilePictureUrl: String?
init(rawResponse: Any?) {
// Decode JSON from rawResponse into other properties here.
guard let response = rawResponse as? Dictionary<String, Any> else {
return
}
if let name = response["name"] as? String {
self.name = name
}
if let id = response["id"] as? String {
self.id = id
}
if let gender = response["gender"] as? String {
self.gender = gender
}
if let email = response["email"] as? String {
self.email = email
}
if let picture = response["picture"] as? Dictionary<String, Any> {
if let data = picture["data"] as? Dictionary<String, Any> {
if let url = data["url"] as? String {
self.profilePictureUrl = url
}
}
}
}
}
And in the success case you can get the values like this:
let connection = GraphRequestConnection()
connection.add(MyProfileRequest()) { response, result in
switch result {
case .success(let response):
print("My facebook id is \(response.id!)") //Make sure to safely unwrap these :)
print("My name is \(response.name!)")
case .failed(let error):
print("Custom Graph Request Failed: \(error)")
}
}
connection.start()
import FBSDKLoginKit //FBSDKLoginKit installs automatically when you install FacebookCore through CocoaPods
///Inside your view controller
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
/// DEFAULT
//fired when fb logged in through fb's default login btn
if error != nil {
print(error)
return
}
showDetails()
}
fileprivate func showDetails(){
FBSDKGraphRequest(graphPath: "/me", parameters: ["fields": "id, name, first_name, last_name, email, gender"]).start { (connection, result, err) in
////use link for more fields:::https://developers.facebook.com/docs/graph-api/reference/user
if err != nil {
print("Failed to start graph request:", err ?? "")
return
}
let dict: NSMutableDictionary = result as! NSMutableDictionary
print("The result dict of fb profile::: \(dict)")
let email = dict["email"] as! String!
print("The result dict[email] of fb profile::: \(email)")
let userID = dict["id"] as! String
print("The result dict[id] of fb profile::: \(userID)")
// self.profileImage.image = UIImage(named: "profile")
let facebookProfileUrl = "http://graph.facebook.com/\(userID)/picture?type=large"
}
}
//make sure you add read permissions for email and public profile
override func viewDidLoad(){
super.viewDidLoad()
loginButtonFromFB.delegate = self //inherit FBSDKLoginButtonDelegate to your class
loginButtonFromFB.readPermissions = ["email", "public_profile"]
}
I'm using alamofire and it's serializing protocols. I have a model and its working great. Now, exactly how do I get an array of that model from these methods?
static func collection(response response: NSHTTPURLResponse, representation: AnyObject) -> [DataObject] {
var daos: [DataObject] = []
if let representation = representation as? [[String: AnyObject]] {
for contentRepresentation in representation {
if let content = DataObject(response: response, representation: contentRepresentation) {
daos.append(content)
}
}
}
return daos
}
class func populateData() {
Alamofire.request(.GET, url)
.responseCollection { (response: Response<[DataObject], NSError>) in
//response.result.value how do i pass this to my viewcontroller?
}
}
Are you getting a JSON response from the request?
If so, you may be able to try something along the lines of this:
Alamofire.request(.GET, url).responseJSON {
response -> () in
switch response.result {
case .Success(let object):
if let urlResponse = response.response {
let dataObjects = collection(response: urlResponse, representation: object)
// Pass the objects back to your view controller here
}
break
case .Failure(let error):
print("Error: ", error)
break
}
}