I am trying to login through the swift.
I am getting the proper data in the form of json and able to extract the data, but when I try to print the success message in the terminal then I need to press the login button twice.
#IBAction func loginButton(sender: UIButton) {
//test.text = usernameText.text
let user = usernameText.text!
let pass = passwordText.text!
let a = "http://www.example.com/app/login.php?username="
let b = "&password="
let c = a + user + b + pass
let url = NSURL(string: "\(c)")!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonResult: NSDictionary = try NSJSONSerialization.JSONObjectWithData(urlContent, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
if let custdetails : [NSDictionary] = jsonResult["data"] as? [NSDictionary] {
for person: NSDictionary in custdetails {
//for (name,value) in person {
// print("\(name) , \(value)")
//print(person.valueForKey("CustId")!)
//}
let userid:String = person.valueForKey("CustId") as! String
print(userid)
//NSUserDefaults.standardUserDefaults().setBool(true, forKey: "IsUserLoggedIn")
NSUserDefaults().setString(userid, forKey: "userName")
NSUserDefaults.standardUserDefaults().synchronize()
self.dismissViewControllerAnimated(true, completion: nil)
self.message = jsonResult["message"] as! String
}
}
} catch {
print("JSON serialization failed")
}
}
}
task.resume()
print(message)
//navingation code
}
I have printed the userid when I get from the json
SO when I click on the login button I can see the id in the console, but cant see the message which I have printed on the second last line
When I again click on the login button then message is printed and the id is also printed again which means my code runs second time.
Thanks for helping me in advance
Any help will be appreciated.
You need to dispatch_async your JSON results. You can read up more here. You can also use AlamoFire or SwiftyJSON to help you with this. These are very good JSON tools. You can google them.
First, declare your userid variable outside of the JSON response. Preferably at the first line of your method. Then update your codes as below.
for person: NSDictionary in custdetails {
userid = person.valueForKey("CustId") as! String
} dispatch_async(dispatch_get_main_queue(),{
print(userid)
if(userid != ""){
self.dismissViewControllerAnimated(true, completion: nil)
self.message = jsonResult["message"] as! String
}
})
Related
Hi I'm trying to get data from a certain JSON API. I can gat a snapshot of all values from the API, which is shown below. But I can't manage to put a specifiek row in a variable. This is the JSON form which I get. I want to print the "Description" value.Can someone help me with this?
And Hier is my code:
func apiRequest() {
let config = URLSessionConfiguration.default
let username = "F44C3FC2-91AF-5FB2-8B3F-70397C0D447D"
let password = "G23#rE9t1#"
let loginString = String(format: "%#:%#", username, password)
let userPasswordData = loginString.data(using: String.Encoding.utf8)
let base64EncodedCredential = userPasswordData?.base64EncodedString()
let authString = "Basic " + (base64EncodedCredential)!
print(authString)
config.httpAdditionalHeaders = ["Authorization" : authString]
let session = URLSession(configuration: config)
var running = false
let url = NSURL(string: "https://start.jamespro.nl/v4/api/json/projects/?limit=10")
let task = session.dataTask(with: url! as URL) {
( data, response, error) in
if let taskHeader = response as? HTTPURLResponse {
print(taskHeader.statusCode)
}
if error != nil {
print("There is an error!!!")
print(error)
} else {
if let content = data {
do {
let array = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(array)
if let items = array["items"] {
if let description = items["Description"] as? [[String:Any]]{
print(description as Any)
}
}
}
catch {
print("Error: Could not get any data")
}
}
}
running = false
}
running = true
task.resume()
while running {
print("waiting...")
sleep(1)
}
}
First of all the array is not an array and not AnyObject, it's a dictionary which is [String:Any] in Swift 3.
let dictionary = try JSONSerialization.jsonObject(with: content) as! [String:Any]
print(dictionary)
I don't know why all tutorials suggest .mutableContainers as option. That might be useful in Objective-C but is completely meaningless in Swift. Omit the parameter.
The object for key itemsis an array of dictionaries (again, the unspecified JSON type in Swift 3 is Any). Use a repeat loop to get all description values and you have to downcast all values of a dictionary from Any to the expected type.
if let items = dictionary["items"] as? [[String:Any]] {
for item in items {
if let description = item["Description"] as? String {
print(description)
}
}
}
Looks like items is an array that needs to be looped through. Here is some sample code, but I want to warn you that this code is not tested for your data.
if let items = array["items"] as? [[String: AnyObject]] {
for item in items {
if let description = item["Description"] as? String{
print("Description: \(description)")
}
}
}
This code above, or some variation of it, should get you on the right track.
use the SwiftyJSON and it would be as easy as json["items"][i].arrayValue as return and array with items Values or json["items"][i]["description"].stringValue to get a string from a row
So I'm designing an application where, like most apps, takes users to the "home page" after a successful login. However, I can't quite figure out how to get it to work. The code for my Login page is as follows:
import UIKit
class LoginVC: UIViewController {
#IBOutlet weak var usernameTxt: UITextField!
#IBOutlet weak var passwordTxt: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
//#IBAction func userLogin(_ sender: AnyObject) {
#IBAction func userLogin(_ sender: AnyObject) {
// if textboxes are empty
if usernameTxt.text!.isEmpty || passwordTxt.text!.isEmpty {
// red placeholders
usernameTxt.attributedPlaceholder = NSAttributedString(string: "Username", attributes: [NSForegroundColorAttributeName: UIColor.red])
passwordTxt.attributedPlaceholder = NSAttributedString(string: "Password", attributes: [NSForegroundColorAttributeName: UIColor.red])
} else {
// shortcuts
let username = usernameTxt.text!.lowercased()
let password = passwordTxt.text!
// send request to mysql db
// Create a user in the mySQL db
// the exclamation at the end means we insist to launch it
// url to php file
let url = NSURL(string: "https://cgi.soic.indiana.edu/~team7/login.php")!
// request to the file
let request = NSMutableURLRequest(url: url as URL)
// method to pass data to this file via the POST method
request.httpMethod = "POST"
// what occurs after the question mark in the url
// body to be appended to url from values in textboxes
let body = "username=\(username)&password=\(password)"
// appends body to request that will be sent
request.httpBody = body.data(using: String.Encoding.utf8)
// launching
URLSession.shared.dataTask(with: request as URLRequest, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) -> Void in
if error == nil {
// get main queue in code process to communicate back
DispatchQueue.main.async(execute: {
// do this unless some error which is caught by catch
do {
// get json result
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
// guard let is the same thing as if let
// asign json to new variable in secure way
// original guard let used
guard let parseJSON = json else {
print("Error while parsing")
return
}
// get id from parseJSON dictionary
let id = parseJSON["id"] as? String
// if there is some id value
if id != nil && response != nil {
print(parseJSON)
// successfully logged in
//let userID = parseJSON["id"] as! String
//let userN = parseJSON["username"] as! String
//let eMail = parseJSON["email"] as! String
//print(parseJSON["username"] ?? String.self)
//let myVC = self.storyboard?.instantiateViewController(withIdentifier: "RetrievalVC") as! RetrievalVC
//myVC.id_Outlet.text = userID
//myVC.full_Outlet.text = userN
//myVC.email_Outlet.text = eMail
//
//self.navigationController?.pushViewController(myVC, animated: true)
}
} catch {
print("Caught an error \(error)")
}
})
// if unable to process request
} else {
print("error: \(error)")
}
}).resume()
//performSegue(withIdentifier: "loginSuccess", sender: LoginVC.self)
}
}
}
I am trying to use
performSegue(withIdentifier: "loginSuccess", sender: LoginVC.self)
In order to perform the segue but I'm not sure where in the code it should go.
Any suggestions or changes I need to make to the code?
It depends on back end logic.I assume that parseJSON["id"] is returned only if user is verified. So you can use this
let id = parseJSON["id"] as? String
// if there is some id value
if id != nil {
// perform segue here
}
You can perform a segue when error is nil and you are response contains data...
if id != nil && response != nil {
performSegue(withIdentifier: "loginSuccess", sender: LoginVC.self)
}
I'm running into a problem when I try to make a request to YQL for stock data, when the symbol (newCompanyStockSymbol) to look up is user-entered. I fetch the stocks in this function:
func handleSave() {
// Fetch stock price from symbol provided by user for new company
guard let newCompanyStockSymbol = stockTextField.text else {
print("error getting text from field")
return
}
var newCompanyStockPrice = ""
let url = URL(string: "https://query.yahooapis.com/v1/public/yql?q=select%20symbol%2C%20Ask%2C%20YearHigh%2C%20YearLow%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22\(newCompanyStockSymbol)%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
} else if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
let json = JSON(data: data!)
if let quotes = json["query"]["results"]["quote"].array {
for quote in quotes {
let ask = quote["Ask"].stringValue
newCompanyStockPrice = ask
}
}
print("new company json: \(json)")
}
guard let newCompanyName = self.nameTextField.text else {
print("error getting text from field")
return
}
guard let newCompanyLogo = self.logoTextField.text else {
print("error getting text from field")
return
}
print("2: The new commpany stock price is: \(newCompanyStockPrice)")
// Call save function in view controller to save new company to core data
self.viewController?.save(name: newCompanyName, logo: newCompanyLogo, stockPrice: newCompanyStockPrice)
self.viewController?.tableView.reloadData()
}
task.resume()
// Present reloaded view controller with new company added
let cc = UINavigationController()
let companyController = CompanyController()
viewController = companyController
cc.viewControllers = [companyController]
present(cc, animated: true, completion: nil)
}
And I use string interpolation to insert \(newCompanyStockSymbol) into the request URL at the appropriate place. However I get a crash and error on that line because it's returning nil, I expect because it's using the URL with \(newCompanyStockSymbol) in there verbatim, instead of actually inserting the value.
Is there another way to do this?
EDIT
And the save function in view controller that's called from handleSave() above if it's helpful:
func save(name: String, logo: String, stockPrice: String) {
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
let entity =
NSEntityDescription.entity(forEntityName: "Company",
in: managedContext)!
let company = NSManagedObject(entity: entity,
insertInto: managedContext)
company.setValue(stockPrice, forKey: "stockPrice")
company.setValue(name, forKey: "name")
company.setValue(logo, forKey: "logo")
do {
try managedContext.save()
companies.append(company)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
tableView.reloadData()
}
Supposing you entered AAPL in your stockTextField, using simply:
let newCompanyStockSymbol = stockTextField.text
results in newCompanyStockSymbol being:
Optional("AAPL")
which is not what you want in your URL string. The critical section ends up like this:
(%22Optional("AAPL")%22)
Instead, use guard to get the value from the text field:
guard let newCompanyStockSymbol = stockTextField.text else {
// handle the error how you see fit
print("error getting text from field")
return
}
Now your URL should be parsed correctly.
--- Additional info ---
I'm not entirely sure of the rules on 'continued conversation' around here, but hopefully editing this will be acceptable... anyway...
Make sure you are following this flow:
func handleSave() {
let newCompanyName = nameTextField.text
let newCompanyStockSymbol = stockTextField.text
let newCompanyLogo = logoTextField.text
var newCompanyStockPrice = ""
// Fetch stock price from symbol provided by user for new company
let url = URL(string: "https://query.yahooapis.com/v1/public/yql?q=select%20symbol%2C%20Ask%2C%20YearHigh%2C%20YearLow%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22\(newCompanyStockSymbol)%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
} else if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
let json = JSON(data: data!)
if let quotes = json["query"]["results"]["quote"].array {
for quote in quotes {
let ask = quote["Ask"].stringValue
newCompanyStockPrice = ask
// task completed, we've parsed the return data,
// so NOW we can finish the save process and
// update the UI
viewController?.save(name: newCompanyName!, logo: newCompanyLogo!, stockPrice: newCompanyStockPrice)
}
}
}
}
task.resume()
}
I'm not testing this, so it might need a tweak, and your .save() function may need to be forced onto the main thread (since it's doing UI updates). But maybe that's a little more clear.
I'm extremely new to Swift and I'm having trouble building a weather app that utilizes the API from a website called openweathermap.org. When the user enter a city and clicks "SUBMIT" they should be able to see a label that displays the description of the weather.
The results in JSON are:
(
{
description = haze;
icon = 50d;
id = 721;
main = Haze;
},
{
description = mist;
icon = 50d;
id = 701;
main = Mist;
}
)
While attempting to debug, I used the code: print(jsonResult["weather"]!) and this allows me to see the above JSON details. However, I can't seem to get it to work when I try to get the description of the weather.
My goal: I am trying to get the description of the weather to display on my app. I am currently getting the error: cannot use optional chaining on non-optional value of type 'Any'. Your help would be most appreciated!
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var cityTextField: UITextField!
#IBOutlet weak var resultLabel: UILabel!
#IBAction func submit(_ sender: AnyObject) {
// getting a url
if let url = URL(string: "http://api.openweathermap.org/data/2.5/weather?q=" + (cityTextField.text?.replacingOccurrences(of: " ", with: "%20"))! + ",uk&appid=08b5523cb95dde0e2f68845a635f14db") {
// creating a task from the url to get the content of that url
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print("error")
} else {
print("no error")
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String:Any]
//print(jsonResult["weather"]!)
if let description = jsonResult["weather"]??[0]["description"] as? String {
DispatchQueue.main.sync(execute:{
self.resultLabel.text = description
})
}
} catch {
print("JSON processing failed")
}
}
}
}
task.resume()
} else {
resultLabel.text = "Couldn't find weather for that city. Please try a different city."
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Try this
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String:Any]
let weather = jsonResult["weather"] as! [[String : Any]]
if let description = weather[0]["description"] as? String {
print(description)
}
You've confused the compiler here by using "??"
if let description = jsonResult["weather"]??[0]
The proper syntax is just to use one "?"
if let description = jsonResult["weather"]?[0]
But then you'll get another error because in the line:
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String:Any]
You said that jsonResult["weather" will give you type Any. Not type Array.
So you need to unwrap as an array like:
if let descriptions = jsonResult["weather"] as? [[String : Any]], let description = descriptions[0]
And so on.
I am working on a simple Flickr app that gets some data from their API and displays it on a tableview instance. Here's a piece of the code for the TableViewController subclass.
var photos = [FlickrPhotoModel]()
override func viewDidLoad() {
super.viewDidLoad()
getFlickrPhotos()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
private func getFlickrPhotos() {
DataProvider.fetchFlickrPhotos { (error: NSError?, data: [FlickrPhotoModel]?) in
//data is received
dispatch_async(dispatch_get_main_queue(), {
if error == nil {
self.photos = data!
self.tableView.reloadData()
}
})
}
}
The application does not seem to load the data if the { tableView.reloadData() } line is removed. Does anyone know why this would happen since I call getFlickrPhotos() within viewDidLoad(). I believe I am also dispatching from the background thread in the appropriate place. Please let me know what I am doing incorrectly.
EDIT -- Data Provider code
class func fetchFlickrPhotos(onCompletion: FlickrResponse) {
let url: NSURL = NSURL(string: "https://api.flickr.com/services/rest/?method=flickr.photos.getRecent&api_key=\(Keys.apikey)&per_page=25&format=json&nojsoncallback=1")!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
if error != nil {
print("Error occured trying to fetch photos")
onCompletion(error, nil)
return
}
do {
let jsonResults = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary
let photosContainer = jsonResults!["photos"] as? NSDictionary
let photoArray = photosContainer!["photo"] as? [NSDictionary]
let flickrPhoto: [FlickrPhotoModel] = photoArray!.map{
photo in
let id = photo["id"] as? String ?? ""
let farm = photo["farm"] as? Int ?? 0
let secret = photo["secret"] as? String ?? ""
let server = photo["server"] as? String ?? ""
var title = photo["title"] as? String ?? "No title available"
if title == "" {
title = "No title available"
}
let model = FlickrPhotoModel(id: id, farm: farm, server: server, secret: secret, title: title)
return model
}
//the request was successful and flickrPhoto contains the data
onCompletion(nil, flickrPhoto)
} catch let conversionError as NSError {
print("Error parsing json results")
onCompletion(conversionError, nil)
}
}
task.resume()
}
I'm not familiar with that API, but it looks like the fetchFlickrPhotos method is called asynchronously on a background thread. That means that the rest of the application will not wait for it to finish before moving on. viewDidLoad will call the method, but then move on without waiting for it to finish.
The completion handler that you provide is called after the photos are done downloading which, depending on the number and size of the photos, could be seconds later. So reloadData is necessary to refresh the table view after the photos are actually done downloading.