I've been trying to display certain JSON data to the storyboard but for some reason have been unable. The part of it that works, is the the var name, which is a string, and it doesn't have to be converted so it just works. The part that I am having an issue with is trying to convert two Int64's to strings, but I have them listed as AnyObjects. It's really confusing me, but here is the issue in code:
The program runs fine, but it doesn't display any information for profileIconId and summonerLevel.
import UIKit
class ViewController: UIViewController, NSURLConnectionDelegate {
lazy var data = NSMutableData()
#IBOutlet weak var searchField: UITextField!
#IBOutlet weak var name: UILabel!
#IBOutlet weak var summonerLevel: UILabel!
#IBOutlet weak var profileIconId: UILabel!
#IBAction func enterButton(sender: AnyObject) {
startConnection()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func startConnection(){
let urlPath: String = "https://na.api.pvp.net/api/lol/na/v1.4/summoner/by-name/soon2challenger?api_key=(removed my private api key for obvious reasons)"
var url: NSURL = NSURL(string: urlPath)!
var request: NSURLRequest = NSURLRequest(URL: url)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)!
connection.start()
}
func connection(connection: NSURLConnection!, didReceiveData data: NSData!){
self.data.appendData(data)
}
func buttonAction(sender: UIButton!){
startConnection()
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
var err: NSError
// throwing an error on the line below (can't figure out where the error message is)
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary
let include: AnyObject = jsonResult.objectForKey(searchField.text)!
var name1: AnyObject = include.objectForKey("name")!
var summLevel: AnyObject = include.objectForKey("summonerLevel")!
var profIconId: AnyObject = include.objectForKey("profileIconId")!
name.text = name1 as? String
profileIconId.text = profIconId as? String
summonerLevel.text = summLevel as? String
println(name1)
println(summLevel)
println(profIconId)
}
}
The code that processes and displays everything is in the connectionDidFinishLoading function at the very bottom of the code.
Here's a refactor of your connectionDidFinishLoading(_:) method that properly unwraps the values using optional bindings.
You might also consider using NSNumberFormatter instead of "\()".
func connectionDidFinishLoading(connection: NSURLConnection) {
var err: NSError?
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary,
let include = jsonResult.objectForKey(searchField.text) as? NSDictionary {
if let name1 = include[ "name" ] as? String {
name.text = name1
println(name1)
}
if let summLevel = include[ "summonerLevel" ] as? NSNumber {
summonerLevel.text = "\(summLevel.integerValue)"
println(summLevel)
}
if let profIconId = include[ "profileIconId" ] as? NSNumber {
profileIconId.text = "\(profIconId.integerValue)"
println(profIconId)
}
}
}
Related
I am using http://www.omdbapi.com/?t=pulp+fiction IMDB api for "pulp fiction" in my Code.
There is a search bar in the application and I write "pulp fiction" to this search bar then enter.
I get this error.
Could not cast value of type '__NSDictionaryM' (0x1055952b0) to
'NSString' (0x1023e3c60).
ViewController.swift:
//
// ViewController.swift
// IMDB Api Project
//
// Created by gurkan on 5.05.2017.
// Copyright © 2017 gurkan. All rights reserved.
//
import UIKit
class ViewController: UIViewController,UISearchBarDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var directorLabel: UILabel!
#IBOutlet weak var ratingLabel: UILabel!
#IBOutlet weak var actorsLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
searchBar.delegate = self
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchForMovie(title: searchBar.text!)
searchBar.text = ""
}
func searchForMovie(title: String){
//http://www.omdbapi.com/?t=pulp+fiction
if let movie = title.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed){
let url = URL(string: "http://www.omdbapi.com/?t=\(movie)")
let session = URLSession.shared
let task = session.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error!)
} else {
if data != nil {
do {
let jsonResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)
as! String //Error Line!
let jsonString = jsonResult.components(separatedBy: "")
let jsonDict = jsonString as! Dictionary<String,String>
DispatchQueue.main.async {
print(jsonDict)
}
} catch {
}
}
}
})
task.resume()
}
}
}
How can I solve this problem?
There's two things wrong.
First, this line force unwraps to a String
let jsonResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)
as! String
Simple remove as! String - it's not needed. Swift will infer the type and correctly create jsonResult.
Second, your code assumes that the JSON response coming back is a dictionary made up entirely of String names, and String values. Looking at the response from the url you posted, the object associated with the Ratings value is actually a dictionary. Since you are force unwrapping jsonString into a dictionary of strings, that will fail, since it is NOT a dictionary of strings. It's a dictionary of Strings and other stuff - a dictionary.
The easiest fix is:
Replace this line:
let jsonDict = jsonString as! Dictionary<String,String>
with
let jsonDict = jsonString as! [String: AnyObject]
I have a log in page that collects a username and password. On submit, Its sends to the database to retrieve our servers access key. I do this through an asynchronous JSON POST using session.dataTask. When I retrieve the JSON Object I parse the key out of it. I want to pass it to the next page, retrieve a firebase token and then send both pieces of data back to the server for DB storage. I have created a "prepare for segue" function that collects the variable and passes it to a variable on the next page. I believe I am not setting up the sequence of events correctly or that the data isn't making it out of the Async container. Can someone have a look at these two files and see where I am getting it wrong?
Here is the first page I want to segue away from after making the REST web service call...
loginVC.swift:
import UIKit
class LoginVC: UIViewController {
#IBOutlet weak var username: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var validationBox: UITextView!
#IBAction func logInAction(_ sender: UIButton) {
guard let user = username.text, !user.isEmpty else {
validationBox.text = "Please enter valid credentials"
return
}
guard let pass = password.text, !pass.isEmpty else {
validationBox.text = "Please enter valid credentials"
return
}
let params = ["sUser": username.text!, "sPass": password.text!]
let url = URL(string: "restWebServiceURL")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else { return }
guard let data = data else { return }
do {
if let parsedJSON = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
let parsedData = parsedJSON["d"] as! [String:Any]
let key = parsedData["key"] as! String
DispatchQueue.main.async {
print(key)
self.performSegue(withIdentifier: "FirebaseVC", sender: key)
}
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}
func sayHello() {
print("Hello!")
}
func sayGoodbye() {
print("Goodbye!")
}
override func viewDidLoad() {
super.viewDidLoad()
validationBox.text = "Ready..."
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let FirebaseInit = segue.destination as? FirebaseVC {
if let sKey = sender as? String {
print("prepare - " + sKey)
FirebaseInit.sessionKey = sKey
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is the page I want to go to to receive the data access key ...
FirebaseVC.swift:
import UIKit
class FirebaseVC: UIViewController {
private var _sessionKey = String()
var sessionKey : String {
get { return _sessionKey }
set { _sessionKey = newValue }
}
#IBOutlet weak var sessionKeyTestBox: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
print(_sessionKey)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Feel free to suggest a better way to pass the data to the next page. Thanks...
It turns out I was correct in my assumption the the chain of events was off. Following the model suggested by #achrefGassoumi, I moved the datatask to a Singleton Service here:
import Foundation
struct CallWebService {
static let sharedInstance = CallWebService()
func logInToCaduceus(u: String, p: String, completion: #escaping (_ sKey: String) -> ()) {
let params = ["sUser": u, "sPass": p]
let url = URL(string: "https://telemed.caduceususa.com/ws/telemed.asmx/telemedLogin")!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
do {
request.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
guard error == nil else { return }
guard let data = data else { return }
do {
if let parsedJSON = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
let parsedData = parsedJSON["d"] as! [String:Any]
let key = parsedData["key"] as! String
DispatchQueue.main.async {
completion(key)
}
}
} catch let error {
print(error.localizedDescription)
}
})
task.resume()
}
}
Then my two controllers look like this:
LoginVC
import UIKit
class LoginVC: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.destination.isKind(of: FirebaseVC.self)) {
let vc = segue.destination as! FirebaseVC
if let sKey = sender as? String {
vc.sessionKey = sKey
}
}
}
#IBOutlet weak var username: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var validationBox: UITextView!
#IBAction func logInAction(_ sender: UIButton) {
guard let user = username.text, !user.isEmpty else {
validationBox.text = "Please enter valid credentials"
return
}
guard let pass = password.text, !pass.isEmpty else {
validationBox.text = "Please enter valid credentials"
return
}
CallWebService.sharedInstance.logInToCaduceus(u: username.text!, p: password.text!, completion: {(sessionKey: String) -> Void in
print(sessionKey)
self.performSegue(withIdentifier: "FirebaseVC", sender: sessionKey)
}
)
}
override func viewDidLoad() {
super.viewDidLoad()
//validationBox.textAlignment = .center
validationBox.text = "Ready..."
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
AND THE receiving FirebaseVC
import UIKit
class FirebaseVC: UIViewController {
private var _sessionKey = String()
var sessionKey : String {
get { return _sessionKey }
set { _sessionKey = newValue }
}
#IBOutlet weak var sessionKeyTestBox: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
sessionKeyTestBox.text = _sessionKey
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Excuse my (non-swift) Javascript terminology but essentially I moved the data call into a service and then place a callback method in the service with the completion method to ensure the the performSegue doesn't fire until the data has been received and parsed out. So when i submit the log in form data to the server the segue doesn't fire until that async call has been completed.
I am trying to display a Json result (temperature and Humidity) on my view controller (respectively temperatureDisp and humidityDisp), but it does not seem to work.
class HomeVC: UIViewController {
#IBOutlet var usernameLabel: UILabel!
#IBOutlet var temperatureDisp: UILabel!
#IBOutlet var humidityDisp: UILabel!
#IBAction func logoutTapped(sender: AnyObject) {
let appDomain = NSBundle.mainBundle().bundleIdentifier
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain!)
self.performSegueWithIdentifier("goto_login", sender: self)
}
override func viewDidAppear(animated: Bool) {
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
let isLoggedIn:Int = prefs.integerForKey("ISLOGGEDIN") as Int
if (isLoggedIn != 1) {
self.performSegueWithIdentifier("goto_login", sender: self)
} else {
self.usernameLabel.text = prefs.valueForKey("USERNAME") as! NSString as String
}
}
override func viewDidLoad() {
super.viewDidLoad()
var url2 : String = "http://admin:xxxxxxx#xxxxxx/xxxxx.fr/metrics2.php"
var request2 : NSMutableURLRequest = NSMutableURLRequest()
request2.URL = NSURL(string: url2)
request2.HTTPMethod = "GET"
NSURLConnection.sendAsynchronousRequest(request2, queue: NSOperationQueue(), completionHandler: {(response: NSURLResponse!,data: NSData!,error: NSError!) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult : NSArray! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: error) as! NSArray
if (jsonResult != nil) {
println(jsonResult)
} else {
println("There is a problem")
}
var temperature = jsonResult[0].valueForKey("temperature") as! String
var humidity = jsonResult[0].valueForKey("humidite") as! String
println(temperature)
println(humidity)
self.humidityDisp.text = temperature
})
}
}}
That is how the variable jsonResult looks :
(
{
Id = 117;
date = "2015-04-06";
humidite = "45.3";
login = raspberrypi;
luminosite = "\U00e9teinte";
temperature = "18.4";
time = "16:25:21";
}
)
I'm not sure what you mean by "it does not seem to work", but from your code I can assume at least that you are not getting the values you expect from your JSON result, but more likely your app is crashing like crazy. If you want to write applications in Swift you absolutely must understand optionals and learn how to properly work with them. Read The Swift Programming Language—and thoroughly. As your code is now, by force unwrapping using as! and using implicitly unwrapped types (those followed by !) you are ignoring the entire concept of optionals and opening yourself up to crashes.
So, assuming that there is no network or parsing errors, and assuming that the JSON string you're parsing has an array as its root object, the following should work. I've taken the liberty of typing and unwrapping your variables appropriately, as well as cleaning up some of the cruft.
class HomeVC: UIViewController {
#IBOutlet var usernameLabel: UILabel!
#IBOutlet var temperatureDisp: UILabel!
#IBOutlet var humidityDisp: UILabel!
#IBAction func logoutTapped(sender: AnyObject) {
if let appDomain = NSBundle.mainBundle().bundleIdentifier {
NSUserDefaults.standardUserDefaults().removePersistentDomainForName(appDomain)
}
self.performSegueWithIdentifier("goto_login", sender: self)
}
override func viewDidAppear(animated: Bool) {
let prefs = NSUserDefaults.standardUserDefaults()
let isLoggedIn = prefs.boolForKey("ISLOGGEDIN")
if isLoggedIn {
self.performSegueWithIdentifier("goto_login", sender: self)
} else {
if let username = prefs.stringForKey("USERNAME") {
self.usernameLabel.text = username
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
var url2 = "http://admin:xxxxxxx#xxxxxx/xxxxx.fr/metrics2.php"
var request2 = NSMutableURLRequest()
request2.URL = NSURL(string: url2)
request2.HTTPMethod = "GET"
NSURLConnection.sendAsynchronousRequest(request2, queue: NSOperationQueue()) { (response: NSURLResponse!,data: NSData!,error: NSError!) in
var parseError:NSError?
if let data = data, let jsonResults = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &parseError) as? NSArray {
println( jsonResults )
if let result = jsonResults.firstObject as? [String : AnyObject] {
if let humidity = result["humidite"] as? String {
self.humidityDisp.text = humidity
}
if let temperature = result["temperature"] as? String {
self.temperatureDisp.text = temperature
}
}
} else {
println("There is a problem: \(parseError)" )
}
}
}
}
I've been trying to return a certain value in the data that I receive when processing JSON data but I can't seem to get the certain value. I can only get all of the information. Here's the problem in greater detail:
Code:
class ViewController: UIViewController, NSURLConnectionDelegate {
lazy var data = NSMutableData()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
startConnection()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func startConnection(){
let urlPath: String = "https://na.api.pvp.net/api/lol/na/v1.4/summoner/by-name/soon2challenger?api_key=(removed my private API key for obvious reasons)"
var url: NSURL = NSURL(string: urlPath)!
var request: NSURLRequest = NSURLRequest(URL: url)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)!
connection.start()
}
func connection(connection: NSURLConnection!, didReceiveData data: NSData!){
self.data.appendData(data)
}
func buttonAction(sender: UIButton!){
startConnection()
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
var err: NSError
// throwing an error on the line below (can't figure out where the error message is)
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary
println(jsonResult)
}
}
The code works fine. This is the result I get.
{
soon2challenger = {
id = 43993167;
name = soon2challenger;
profileIconId = 844;
revisionDate = 1435549418000;
summonerLevel = 30;
};
}
The problem is that when I want to return a certain value from the list, as in the id, I can't seem to do that. I tried doing println(jsonResult["id"]) but that just results in nil.
How can I return a certain value from the list? Ex. id, name, profileIconId
Try using objectForKey like so:
let inside = jsonResult.objectForKey("soon2challenger")
Now you have extracted the soon2challenger object. Then you can easily get values:
print(include.objectForKey("id"))
You are trying to access a value inside a sub-array. The previous poster is correct; after getting the sub-array you can use array["id"];
Exactly, as title. I started by putting my HTTP request session in the viewdidload, until I realized it was a terrible place to put it, as it's way low in the hierarchy of methods called, and essentially it was hopeless to define variables there to pass to another VC.
However, I switched to Viewdidappear, but I still get nil, when I pass the variables through prepareforsegue. Is there another method I can use, or how do you suggest I achieve what I want? I could possibly post request whenever i press a button, that would surely pass variables down?
override func viewDidLoad() {
super.viewDidLoad()
activityIndicatorView.startAnimating()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let request = NSMutableURLRequest(URL: NSURL(string: "http://127.0.0.1:5000")!)
request.HTTPMethod = "POST"
let postString = "color=\(self.finalDataPassed)&location=\(self.thirdDataPassed)&weather=\(self.dataPassed)&city=\(self.secondDataPassed)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
println("response = \(response)")
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println(responseString)
var error: NSError?
let result = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as? NSDictionary
var dict: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as! NSDictionary
if(error != nil) {
println(error!.localizedDescription)
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: '\(jsonStr)'")
}
else {
if let results = result?.valueForKey("result") as? NSArray {
dispatch_async(dispatch_get_main_queue(), {
println(results)
})
}
let parseJSON: AnyObject? = result!.valueForKey("result")
var stuff: AnyObject! = parseJSON!.valueForKey("price")
var otherstuff: NSString! = parseJSON!.valueForKey("YlowestURL") as! NSString
}
self.activityIndicatorView.stopAnimating()
self.performSegueWithIdentifier("calculateToDisplaySegue", sender: nil)
}
task.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
var stuff: String!
var otherstuff: String!
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "calculateToDisplaySegue"){
var svc = segue.destinationViewController as! PriceViewController;
svc.dataPassed = self.stuff
svc.secondDataPassed = self.otherstuff
}
}
stuff and otherstuff are defined local to the method, not in the class instance variables.
IOW use:
self.stuff = parseJSON!.valueForKey("price")
self.otherstuff = parseJSON!.valueForKey("YlowestURL") as! NSString
Of course stuff and otherstuff must be declared as ivars of the appropriate type in place of:
var stuff: String!
var otherstuff: String!
It seems you're redefining your class properties with local variables inside the handler. You should replace
var stuff: AnyObject! = parseJSON!.valueForKey("price")
var otherstuff: NSString! = parseJSON!.valueForKey("YlowestURL") as! NSString
by
self.stuff = parseJSON!.valueForKey("price") as! String
self.otherstuff = parseJSON!.valueForKey("YlowestURL") as! String