Swift3 - How to access variables in closure - json

Im trying to access the variable pic after the request is made but its in a closure, thats why print(pic) doesn't work.
How would someone go about accessing this?
guard let url = URL(string: "myurl") else{ return }
var pic = ""
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? NSDictionary
if let parseJSON = json {
pic = parseJSON["picture"] as! String
print(json!)
}
} catch {
print(error)
}
}
}.resume()
print(pic)
}

Assuming pic is an image you'll be loading into a UIImageView:
You can add an Activity Indicator to your ImageView. Then when you call your function to download the pic simply add:
guard let url = URL(string: "myurl") else{ return }
activityIndicator.isHidden = false
activityIndicator.startAnimating()
The user will know a download is occurring. When complete,
DispatchQueue.main.async {
activityIndicator.isHidden = true
activityIndicator.stopAnimating()
myImageView.image = UIImage(named: "pic")
}
}.resume
Dispatching on the main que will update the UI immediately.

Related

Need to reduce json data fetching time using Alamofire in swift

In my application I am working on Alamofire to fetch the data . I think it is taking little bit more time to fetch json data. I am adding my code here. Can anyone suggest me how to reduce the fetching time?
func getUserData(completion: #escaping LcodeResponseCompletion) {
guard let url = URL(string: "\(LCODE_URL)") else{return}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = HTTPMethod.get.rawValue
Alamofire.request(urlRequest).responseJSON { (response) in
if let error = response.result.error {
debugPrint(error.localizedDescription)
completion(nil)
return
}
guard let data = response.data else { return completion(nil)}
let jsonDecoder = JSONDecoder()
do {
let lcode = try jsonDecoder.decode(Empty.self, from: data)
completion(lcode)
} catch {
debugPrint(error.localizedDescription)
completion(nil)
}
}
}

Load data from server using swift 4

I try to load the user profile like below
#IBAction func Btn_LoadDataFromDataBase(_ sender: UIButton) {
let myurl = "site.com/profile.php"
LoadURL(url: myurl)
}
func LoadURL(url: String) {
do{
let appURL = URL(string: url)! // convert string to URL
let data = try Data(contentsOf: appURL)
//error here on this line below :
let json1 = try JSONSerialization.jsonObject(with: data ) as! [String: Any]
print(json1)
let query1 = json1["profile"] as! [String: Any]
print(query1)
label_email.text = "Email : (query1["email"]!)"
}catch{
print("error in url")
}
}
if I test the json via webbrowser I get it like this:
{profile : [{"0":"999","id":"999","1":"1","email":"blabla#gmail.com","2":"1111","tel":"00122222222","3":"0" ..........
php code:
print "{profile : ".json_encode($user_profile,JSON_UNESCAPED_UNICODE)."}";
mysql_close($db);
?>
Please read the JSON carefully, there are only two different collection types
{} is dictionary ([String: Any])
[] is array ([Any] but in most cases [[String: Any]])
so the result for query1 (I changed the variable names to something more descriptive) is an array and you need a loop to print all elements:
let profileData = try JSONSerialization.jsonObject(with: data ) as! [String: Any]
let profiles = profileData["profile"] as! [[String: Any]] // could be even [[String:String]]
for profile in profiles {
print("Email :", profile["email"]!")
}
I'm wondering why so many owners of web services send the PHP arrays unnecessarily with both index and key.
And never load data from a remote URL synchronously, use asynchronous URLSession
You're better using URLRequest for async requests. You will need to pass your appURL as a parameter in a URLRequest and handle the answer in its completion handler.
An example:
let urlString = "https://swift.mrgott.pro/blog.json"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else { return }
// Implement JSON decoding and parsing
do {
let articlesData = try JSONDecoder().decode([OBJECT YOU WANT].self, from: data)
} catch let jsonError {
print(jsonError)
}
}.resume()

Swift JSON parsing (getting nothing)

I successfully get data from RSS feeds all the time but I am having a problem with this one and for the life of me I can't tell what the issue is.
Below code executes, returns 200 status code and no content
let urlTMC = URL(string: "https://teslamotorsclub.com/tmc/forums/model-s.73/index.rss")!
let urlRequest = NSMutableURLRequest(url: urlTMC as URL)
let session = URLSession.shared
let task = session.dataTask(with: urlRequest as URLRequest, completionHandler: { data, response, error in
print(data)
print(response)
print(error)
if let response = response, let data = data {
do {
let jsonData = JSON(data: data)
print(jsonData)
} catch {
print("JSONSerial error")
}
}
else {
print(error!)
}
DispatchQueue.main.async(execute: {
print("DISPATCH")
})
})
task.resume()

Swift 3 - Activity Indicator delay or not responding

I have field and button when you fill the field with content and click the button:
#IBAction func dpSomething(_ sender: UIButton) {
activityIndicator.startAnimating()
activityIndicator.isHidden = false
field.isHidden = true
send.isHidden = true
let url = "http://urlwithjson"
let session = URLSession.shared
DispatchQueue.main.async { [unowned self] in
let task = session.dataTask(with: URL(string: url)!) { (data, response, error) in
if(error==nil){
do {
let jsonData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! NSDictionary
if((jsonData["response"] as! Int) == 0){
self.responseL.text = "no results"
}
self.activityIndicator.stopAnimating()
self.responseL.isHidden = false
} catch _ {}
}
}
task.resume()
}
}
when I use print it prints me almost immediately the JSON, but activity indicator needs 10-20 secs to disappear or sometimes it doesn't disappear. Can somebody tells me why? And also when I change the text in the label I receive error in console:
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.
Why this is happening ?
First of all Add the following code after activityIndicator.startAnimating() , this code will hide the activity indicator ones you call self.activityIndicator.stopAnimating():
activityIndicator.hidesWhenStopped = true
Also you are only hiding the activity indicator if the response succeeded, so you may need to hide the indicator when there is an error in catch block and in else block when you get an error from api response
Another point is when you make your request it's not supposed to be in main thread, only in background and then when you need to update the UI then do use main thread , so finally here is the full code you may need:
#IBAction func dpSomething(_ sender: UIButton) {
activityIndicator.startAnimating()
activityIndicator.isHidden = false
activityIndicator.hidesWhenStopped = true
field.isHidden = true
send.isHidden = true
let url = "http://urlwithjson"
let session = URLSession.shared
let task = session.dataTask(with: URL(string: url)!) { (data, response, error) in
if(error==nil){
do {
let jsonData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! NSDictionary
if((jsonData["response"] as! Int) == 0){
DispatchQueue.main.async {
//Update your UI here
self.responseL.text = "no results"
}
}
DispatchQueue.main.async {
//Update your UI here
self.activityIndicator.stopAnimating()
self.responseL.isHidden = false
}
} catch _ {
// JSONSerialization error might happen so hide the indicator
DispatchQueue.main.async {
//Update your UI here
self.activityIndicator.stopAnimating()
self.responseL.isHidden = false
}
}
}else {
// api response error might happen so hide the indicator
DispatchQueue.main.async {
//Update your UI here
self.activityIndicator.stopAnimating()
self.responseL.isHidden = false
}
}
}
task.resume()
}
You need to only make UI changes on main thread never call Async API call on main thread.
let task = session.dataTask(with: URL(string: url)!) { (data, response, error) in
if(error==nil){
do {
let jsonData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! NSDictionary
if((jsonData["response"] as! Int) == 0){
self.responseL.text = "no results"
}
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true
self.responseL.isHidden = false
}
} catch _ {}
}
}
task.resume()

Finishing the completion handler that fetches JSON from web, SWIFT [duplicate]

I am currently trying to download, parse and print JSON from an URL.
So far I got to this point:
1) A class (JSONImport.swift), which handles my import:
var data = NSMutableData();
let url = NSURL(string:"http://headers.jsontest.com");
var session = NSURLSession.sharedSession();
var jsonError:NSError?;
var response : NSURLResponse?;
func startConnection(){
let task:NSURLSessionDataTask = session.dataTaskWithURL(url!, completionHandler:apiHandler)
task.resume();
self.apiHandler(data,response: response,error: jsonError);
}
func apiHandler(data:NSData?, response:NSURLResponse?, error:NSError?)
{
do{
let jsonData : NSDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary;
print(jsonData);
}
catch{
print("API error: \(error)");
}
}
My problem is, that the data in
do{
let jsonData : NSDictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary;
print(jsonData);
}
remains empty.
When I debug,the connection starts successfully, with the given url as a parameter. But my jsonData variable doesn't get printed. Instead the catch block throws the error, stating that there is no data in my variable:
API error: Error Domain=NSCocoaErrorDomain Code=3840 "No value."
Can someone please help me with this?
What am I missing?
Thank you all very much in advance!
[Edited after switching from NSURL Connection to NSURLSession]
Here's an example on how to use NSURLSession with a very convenient "completion handler".
This function contains the network call and has the "completion handler" (a callback for when the data will be available):
func getDataFrom(urlString: String, completion: (data: NSData)->()) {
if let url = NSURL(string: urlString) {
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { (data, response, error) in
// print(response)
if let data = data {
completion(data: data)
} else {
print(error?.localizedDescription)
}
}
task.resume()
} else {
// URL is invalid
}
}
You can use it like this, inside a new function, with a "trailing closure":
func apiManager() {
getDataFrom("http://headers.jsontest.com") { (data) in
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: [])
if let jsonDict = json as? NSDictionary {
print(jsonDict)
} else {
// JSON data wasn't a dictionary
}
}
catch let error as NSError {
print("API error: \(error.debugDescription)")
}
}
}