I am creating a project where I have to display the YouTube search API response in a UI Text View when a button is clicked. Now I am getting the response in console.
1) How to display it as a JSON response.
2) How to display it in UI text view.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var resultTextView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func goButton(_ sender: Any) {
guard let url = URL(string: "https://www.youtube.com/watch?v=6Zf79Ns8_oY")else {
return
}
let session = URLSession.shared
let task = session.dataTask(with: url) {(data, response, error) in
if let response = response {
print(response)
}
if let jsondata = data {
print(jsondata)
}
}
task.resume()
}
}
I think you are looking for that:
Parse Response using JSONSerialization and set required value to textview text.
guard let url = URL(string: "https://www.youtube.com/watch?v=6Zf79Ns8_oY")else {
return
}
let session = URLSession.shared
let task = session.dataTask(with: url) {(data, response, error) in
guard let data = data, error == nil else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String:Any]
let text = json["KEY"] as? String
DispatchQueue.main.async{
self.YOURTEXTVIEW.text = text
}
} catch let error as NSError {
print(error)
}
}
task.resume()
maybe it is helpful.
Related
I'm downloading remote JSON data and want my loading screen to stay up until the download is complete. Once my parse method finishes running, a segue should be called to move to the next view automatically.
I've verified that my data is properly downloading and parsing. My performSegue function is even being called when I throw up a breakpoint. But the application is still not moving to the next view.
Here's where I'm calling my parse method and then immediately calling the desired segue:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
downloadSources(atURL: "https://newsapi.org/v1/sources?language=en")
performSegue(withIdentifier: "loadingFinished", sender: self)
}
For reference, if you need it, here is my parse method in its entirety:
func downloadSources(atURL urlString: String) {
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
if let validURL = URL(string: urlString) {
var request = URLRequest(url: validURL)
request.setValue("49fcb8e0fa604e7aa461ee4f22124177", forHTTPHeaderField: "X-Api-Key")
request.httpMethod = "GET"
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
assertionFailure()
return
}
guard let response = response as? HTTPURLResponse,
response.statusCode == 200,
let data = data
else {
assertionFailure()
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
guard let sources = json["sources"] as? [[String: Any]]
else {
assertionFailure()
return
}
for source in sources {
guard let id = source["id"] as? String,
let name = source["name"] as? String,
let description = source["description"] as? String
else {
assertionFailure()
return
}
self.sources.append(Source(id: id, name: name, description: description))
}
}
}
catch {
print(error.localizedDescription)
assertionFailure()
}
}
task.resume()
}
}
Thanks in advance.
Sounds like a closure callback is what you want.
typealias CompletionHandler = ((_ success:Bool) -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
downloadSources(atURL: "www.example.com", completion: {
if success {
performSegue(withIdentifier: "loadingFinished", sender: self)
return
}
// otherwise deal with failure
})
}
func downloadSources(atURL urlString: String, completion: CompletionHandler) {
if error != nil {
completion?(false)
return
}
// finish downlaod
completion?(true)
}
I have a table in a view controller that is populated through a dictionary from which information is retrieved via a JSON request. In the viewDidLoad() function, I call the function that retrieves the data which is added to `IncompletedDeadlines dictionary:
override func viewDidLoad() {
super.viewDidLoad()
self.IncompleteDeadlines = [String:AnyObject]()
self.retrieveIncompletedDeadlines()
}
Everything works however the table only shows when interacted with. I thought maybe the best way to show the table the moment the view appears is by adding a tableView.reload to viewDidAppear as so:
override func viewDidAppear(_ animated: Bool) {
self.tableView.reloadData()
}
But this doesn't fix it. I have attached pictures for clarity of the situation. Picture one shows the view the moment the view appears. Picture 2 only happens once the table is interacted with i.e. swiped. So my question is how can I get the table to show immediately? I understand there can be a delay because of the load, but I shouldn't have to interact with it for it to show:
When the view is interacted with i.e. swiped:
The retrieveIncompletedDeadlines() function is as so:
func retrieveIncompletedDeadlines(){
let myUrl = NSURL(string: "https://www.example.com/scripts/retrieveIncompleteDeadlines.php");
let request = NSMutableURLRequest(url:myUrl! as URL)
let user_id = UserDetails[0]
request.httpMethod = "POST";
let postString = "user_id=\(user_id)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(String(describing: error))")
return
}
var err: NSError?
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
let checker:String = parseJSON["status"] as! String;
if(checker == "Success"){
let resultValue = parseJSON["deadlines"] as! [String:AnyObject]
self.IncompleteDeadlines = resultValue
}
self.tableView.reloadData()
}
} catch let error as NSError {
err = error
print(err!);
}
}
task.resume();
self.tableView.reloadData()
}
JSON will be parsed on the background thread but any update to the UI must be done on the main thread hence you have to do it inside DispatchQueue.main.async {} This article explains well what is the problem.
Furthermore I would write a completions handler which returns the data once the operation has finished. This is another interesting article about.
Completion handlers are super convenient when your app is doing something that might take a little while, like making an API call, and you need to do something when that task is done, like updating the UI to show the data.
var incompleteDeadlines = [String:AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
//please note your original function has changed
self.retrieveIncompletedDeadlines { (result, success) in
if success {
// once all the data has been parsed you assigned the result to self.incompleteDeadlines
self.incompleteDeadlines = result
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
func retrieveIncompletedDeadlines(_ completion:#escaping ([String:AnyObject] , _ success: Bool)-> Void){
let myUrl = NSURL(string: "https://www.example.com/scripts/retrieveIncompleteDeadlines.php");
let request = NSMutableURLRequest(url:myUrl! as URL)
let user_id = UserDetails[0]
request.httpMethod = "POST";
let postString = "user_id=\(user_id)";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request as URLRequest) {
data, response, error in
if error != nil {
print("error=\(String(describing: error))")
return
}
var err: NSError?
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
let checker:String = parseJSON["status"] as! String;
var resultValue = [String:AnyObject]()
if(checker == "Success"){
resultValue = parseJSON["deadlines"] as! [String:AnyObject]
}
completion(resultValue, true)
}
} catch let error as NSError {
err = error
print(err!);
}
}
task.resume();
}
}
My error should be quite obvious but I can't find it;
I've a global variable initialized a the beginning of my class:
class InscriptionStageViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
var lesSemaines = [String]()
I try to populate this array with a distant json file using that function
func getSemainesStages(){
let url = URL(string: "http://www.boisdelacambre.be/ios/json/semaines.json")
let task = URLSession.shared.dataTask(with: url!){ (data, response, error) in
if let content = data {
do {
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
let listeSemaines = myJson["semaine"] as! [[String:AnyObject]]
//print(listeSemaines)
for i in 0...listeSemaines.count-1 {
var tabSem = listeSemaines[i]
let intituleSemaine:String = tabSem["intitule"] as! String
//let dateSemaine:String = tabSem["date"] as! String
DispatchQueue.main.sync
{
self.lesSemaines.append(intituleSemaine)
}
}
} catch
{
print("erreur Json")
}
}
}
task.resume()
}
When I call my function in the viewDidLoad and then I print my global array, it's empty (the URL is correct, the json data is read correctly and when I read the data appended in the array in the loop, it print the (so) needed value...)
Thanks in advance
The download takes time. Introduce another methode:
func updateUi() {
print(lesSemaines)
//pickerView.reloadAllComponents()
}
And call it after the download finished:
func getSemainesStages(){
// ... your code
let task = URLSession.shared.dataTask(with: url!){ (data, response, error) in
// ... your code
for tabSem in listeSemaines{
guard let intituleSemaine = tabSem["intitule"] as? String else {
print("erreur Json")
continue
}
self.lesSemaines.append(intituleSemaine)
}
// update UI *after* for loop
DispatchQueue.main.async {
updateUi()
}
// ... your code
}
}
I have updated your code to Swift 3. Please replace it with below code.
func getSemainesStages(){
let url = URL(string: "http://www.boisdelacambre.be/ios/json/semaines.json")
let task = URLSession.shared.dataTask(with: url!){ (data, response, error) in
if let content = data {
do {
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: Any]
let listeSemaines = myJson["semaine"] as! [[String: Any]]
for i in 0...listeSemaines.count-1 {
var tabSem = listeSemaines[i]
let intituleSemaine:String = tabSem["intitule"] as! String
self.lesSemaines.append(intituleSemaine)
}
print(self.lesSemaines)
} catch {
print("erreur Json")
}
}
}
task.resume()
}
This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed 6 years ago.
I'm parsing JSON data from a remote service. i wrote a function wich do the parsing process. This function has a return value. The result is created in this function and saved in a global property. But when i call the function in viewDidLoad i get an empty result:
Here is my code
class ViewController: UIViewController {
var rates = [String:AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
print(getRates("USD")) // <- Gives me an empty Dictionary
}
func getRates(base: String) -> [String:AnyObject]{
let url = NSURL(string: "http://api.fixer.io/latest?base=\(base)")!
let urlSession = NSURLSession.sharedSession()
let task = urlSession.dataTaskWithURL(url) { (data, response, error) in
do{
self.rates = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as! [String:AnyObject]
//print(self.rates) //<-- Gives me the right output, but i want to use it outside.
}
catch{
print("Something went wrong")
}
}
task.resume()
return self.rates //<- returns an empty Dictionary
}
I can only get the right result inside the function, but I can't use it outside. What is wrong here?
EDIT:
Tank you! All answers are working, but is there a way to store the result in a global property so that i can use the result anywhere? Assuming i have a tableView. Then i need to have the result in a global property
You cannot return response value at once - you have to wait until response arrives from network. So you have to add a callback function (a block or lambda) to execute once response arrived.
class ViewController: UIViewController {
var rates = [String:AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
getRates("USD"){(result) in
print(result)
}
}
func getRates(base: String, callback:(result:[String:AnyObject])->()){
let url = NSURL(string: "http://api.fixer.io/latest?base=\(base)")!
let urlSession = NSURLSession.sharedSession()
let task = urlSession.dataTaskWithURL(url) { (data, response, error) in
do{
self.rates = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as! [String:AnyObject]
callback(self.rates)
//print(self.rates) //<-- Gives me the right output, but i want to use it outside.
}
catch{
print("Something went wrong")
}
}
task.resume()
}
Because you are using NSURLSession and the task is asynchronous you will need to use a completion handler. Here is an example:
//.. UIViewController Code
var rates = [String: AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
getRates("USD") { [weak self] result in
self?.rates = result
}
}
func getRates(base: String, completion: [String: AnyObject] -> Void) {
let url = NSURL(string: "http://api.fixer.io/latest?base=\(base)")!
let urlSession = NSURLSession.sharedSession()
let task = urlSession.dataTaskWithURL(url) { (data, response, error) in
do {
let rates = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as! [String:AnyObject]
completion(rates)
}
catch {
print("Something went wrong")
}
}
task.resume()
}
Try this on your code:
class ViewController: UIViewController {
var rates = [String:AnyObject]()
override func viewDidLoad() {
super.viewDidLoad()
getRates() { (result) in
print(result)
}
}
func getRates(completion: (result: Array)) -> Void{
let url = NSURL(string: "http://api.fixer.io/latest?base=\(base)")!
let urlSession = NSURLSession.sharedSession()
let task = urlSession.dataTaskWithURL(url) { (data, response, error) in
do{
self.rates = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as! [String:AnyObject]
completion(self.rates)
}
catch{
print("Something went wrong")
}
}
task.resume()
return self.rates //<- returns an empty Dictionary
}
}
So I've written a function in swift which gives me a numeric value from JSON api. My question is how can I take the value from function so I can use it in more practical means.
override func viewDidLoad() {
super.viewDidLoad()
getJSON()
}
func getJSON(){
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error == nil{
let swiftyJSON = JSON(data: data!)
let usdPrice = swiftyJSON["bpi"]["USD"]["rate"].doubleValue
print(usdPrice)
}else{
print("There was an error!")
}
let usdPrice gets me the value so how can I take that from the function getJSON() and do something with it, for example attribute it to some label in Main.storyboard
Unfortunately the other answers are incorrect. Just returning a value will not work because you are getting the value from the completion closure of dataTaskWithRequest.
Having the statement return usdPrice should be a compiler error because the completion closure does not have a return value.
You'll need to add your own completion closure to getJSON that gets the double as a parameter.
override func viewDidLoad() {
super.viewDidLoad()
getJSON { (usdPrice) -> Void in
// do something with usdPrice
print(usdPrice)
}
}
func getJSON(completion: (Double) -> Void) {
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error == nil{
let swiftyJSON = JSON(data: data!)
let usdPrice = swiftyJSON["bpi"]["USD"]["rate"].doubleValue
completion(usdPrice)
} else {
print("There was an error!")
}
}
Your function needs to return the value as a result of its execution. For more details check out this: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-ID160
You have to have a return value for the function. The code below should work.
func getJSON() -> Double {
let url = NSURL(string: baseURL)
let request = NSURLRequest(URL: url!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
var usdReturnValue : Double = 0.0
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error == nil{
let swiftyJSON = JSON(data: data!)
let usdPrice = swiftyJSON["bpi"]["USD"]["rate"].doubleValue
print(usdPrice)
usdReturnValue = usdPrice
}else{
print("There was an error!")
}
}
return usdReturnValue
}