How to set JSON output into UILabel in Swift 3.0 ? - ios

I have these JSON data.
(
{
email = "b#p.com.my";
login = ID001;
pw = 1234;
},
{
email = "p#d.com.my";
login = ID002;
pw = 12345;
}
)
Right now, I can only print myJSON value in x code output.
My question is, how to display each JSON into UILabel _email, _login, _pw?
Someone said that I need to store as variable and set into UILabel but i don't know how to do it. Appreciate if someone can help me on this matters.
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet var _email: UILabel!
#IBOutlet var _login: UILabel!
#IBOutlet var _pw: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://localhost/get.php")
let task = URLSession.shared.dataTask(with: url!) {
(data, response, error) in
if error != nil {
print("Error")
return
}
else {
if let content = data {
do {
let myJSON = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(myJSON)
}
catch {}
}
}
}
task.resume()
}
}
Thanks.

EDIT:
After the block where you print your myJSON, try this:
for eachItem in myJSON {
if let emailParsed = eachItem["email"] as? String {
print(emailParsed)
_email.text = emailParsed
}
}
This loop runs between all the dictionaries and
This solution could be helpful for you. Please go through it!

Hi Please try this one
for i..0 < myJson.count {
let object = myJson[i] as AnyObject
if let emailParsed = myJSON["email"] as? String {
_email.text = emailParsed
}
}
EDITED
for i..0 < myJson.count {
let object = myJson[i] as [String : AnyObject]
if let emailParsed = myJSON["email"] as? String {
_email.text = emailParsed
}
}

It's simple because your json object gives an array of dictionary so first you have to take first object from array and after that you can take the string passing the key
override func viewDidLoad()
{
super.viewDidLoad()
let url = URL(string: "http://localhost/get.php")
let task = URLSession.shared.dataTask(with: url!)
{
(data, response, error) in
if error != nil
{
print("Error")
return
}
else
{
if let content = data
{
do {
let myJSON = try JSONSerialization.jsonObject(with: content, options:JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
// print(myJSON)
for data in myJSON
{
let email = data.1["email"] as? String
_email.text = email
}
}
catch {}
}
}
}
task.resume()
}
}
For batter option you can use some predefined libraries like swiftyJSON. To get best result.

Related

Swift - Load/save from CoreData generates duplicate entries

I have run into a problem where I can save and load into and from CoreData in Swift for my iOS app, but I run into a problem where I have tried to guard for duplicate entries, but it does not seem to work. can anyone tell me where I went wrong? Thanks!
My ViewController class:
import UIKit
import CoreData
class ViewController: UIViewController, UITableViewDelegate,
UITableViewDataSource {
#IBOutlet weak var headerLabel:UILabel!
#IBOutlet weak var myTableView: UITableView!
var lenders = [LenderData]()
var lendersTemp = [LenderData]()
override func viewDidLoad() {
super.viewDidLoad()
self.myTableView.rowHeight = 90
myTableView.delegate = self
myTableView.dataSource = self
let fetchRequest: NSFetchRequest<LenderData> = LenderData.fetchRequest()
do {
let lenders = try PersistenceService.context.fetch(fetchRequest)
self.lenders = lenders
} catch {
// Who cares....
}
downloadJSON {
for tempLender in self.lendersTemp {
if !self.lenders.contains(where: {$0.id == tempLender.id}) {
self.lenders.append(tempLender)
}
}
self.lendersTemp.removeAll()
PersistenceService.saveContext()
self.myTableView.reloadData()
}
}
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "https://api.kivaws.org/v1/loans/newest.json")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil {
print("JSON not downloaded")
} else {
if let content = data {
do {
let myJSONData = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
var imageID:Int64 = -1
var country:String = "N/A"
var latLongPair:String = "0.000000 0.000000"
var town:String = "N/A"
if let loans = myJSONData["loans"] as? NSArray {
for i in 0...loans.count-1 {
if let lender = loans[i] as? NSDictionary {
if let imageData = lender["image"] as? NSDictionary { imageID = imageData["id"] as! Int64 }
if let countryData = lender["location"] as? NSDictionary {
country = countryData["country"] as! String
town = countryData["town"] as! String
if let geo = countryData["geo"] as? NSDictionary {
latLongPair = geo["pairs"] as! String
}
}
let newLender = LenderData(context: PersistenceService.context)
newLender.id = lender["id"] as! Int64
newLender.name = lender["name"] as? String
newLender.image_id = imageID
newLender.activity = lender["activity"] as? String
newLender.use = lender["use"] as? String
newLender.loan_amount = lender["loan_amount"] as! Int32
newLender.funded_amount = lender["funded_amount"] as! Int32
newLender.country = country
newLender.town = town
newLender.geo_pairs = latLongPair
self.lendersTemp.append(newLender)
}
}
}
DispatchQueue.main.async {
completed()
}
} catch {
print("Error occured \(error)")
}
}
}
}
task.resume()
}
}
EDIT
Added the part of the code where I populate the lendersTemp array
I quote matt on this one from the comments:
So... You are appending to self.lendersTemp on a background thread but reading it on the main thread. Instead, get rid of it and just pass the data right thru the completed function.
Which is exactly what I did. And this worked

Swift 3/iOS UIView not updating after retrieving remote JSON data

I have a UITableView with a list of users. When you tap on a row, the uid of the user is passed to the UIViewController detail view. A URLRequest is made to retrieve JSON data of the user (username, avatar, etc). However, the detail view inconsistently updates the information. Sometimes it'll show the users' name, avatar, etc but other times it'll show nothing or it'll only show the username or only show the avatar, etc.
In the fetchUser() method, I have a print("Username: \(self.user.username)") that shows the correct data is being retrieved 100% of the time but it won't display it 100% of the time in the view.
Any help would be greatly appreciated.
Thanks!
class ProfileViewController: UIViewController {
#IBOutlet weak var avatarImageView: UIImageView!
#IBOutlet weak var usernameLabel: UILabel!
#IBOutlet weak var networthLabel: UILabel!
var user: User!
var uid: Int?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
fetchUser()
}
func reloadView() {
self.usernameLabel.text = user.username
self.networthLabel.text = "$" + NumberFormatter.localizedString(from: Int((user.networth)!)! as NSNumber, number: NumberFormatter.Style.decimal)
self.avatarImageView.downloadImage(from: user.avatar!)
circularImage(photoImageView: self.avatarImageView)
}
func fetchUser() {
// Post user data to server
let myUrl = NSURL(string: "http://localhost/test/profile")
let urlRequest = NSMutableURLRequest(url: myUrl! as URL);
urlRequest.httpMethod = "POST"
let postString = "uid=\(uid!)"
urlRequest.httpBody = postString.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: urlRequest as URLRequest) { (data, response, error) in
if (error != nil) {
print("error=\(String(describing: error))")
return
} // end if
self.user = User()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String : AnyObject]
if let parseJSON = json?["data"] as? [[String : AnyObject]] {
for userFromJson in parseJSON {
let userData = User()
if let uid = userFromJson["uid"] as? String,
let username = userFromJson["username"] as? String,
let networth = userFromJson["networth"] as? String,
let avatar = userFromJson["avatar"] as? String {
userData.uid = Int(uid)
userData.username = username
userData.networth = networth
userData.avatar = avatar
self.usernameLabel.text = username
self.networthLabel.text = networth
self.avatarImageView.downloadImage(from: avatar)
circularImage(photoImageView: self.avatarImageView)
} // end if
self.user = userData
} // end for
} // end if
DispatchQueue.main.async {
print("Username: \(self.user.username)")
self.reloadView()
}
} catch let error {
print(error)
}
}
task.resume()
}
Firstly, call fetch user in viewWillAppear like this:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
fetchUser()
}
Then, change the code here like I did, don't use the reloadView function you had, instead, update the UI elements on the main thread at the end of the fetchUser function. I also changed it so you weren't updating the UI twice because you have 4 lines at the bottom of the if let uid = ... statement in fetchUser which updated UI elements that wasn't in the main thread which is why in my version I removed those 4 lines of code. Let me know if this worked for you.
let task = URLSession.shared.dataTask(with: urlRequest as URLRequest) { (data, response, error) in
if (error != nil) {
print("error=\(String(describing: error))")
return
} // end if
self.user = User()
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String : AnyObject]
if let parseJSON = json?["data"] as? [[String : AnyObject]] {
for userFromJson in parseJSON {
let userData = User()
if let uid = userFromJson["uid"] as? String,
let username = userFromJson["username"] as? String,
let networth = userFromJson["networth"] as? String,
let avatar = userFromJson["avatar"] as? String {
userData.uid = Int(uid)
userData.username = username
userData.networth = networth
userData.avatar = avatar
} // end if
self.user = userData
} // end for
} // end if
DispatchQueue.main.async {
self.usernameLabel.text = user.username
self.networthLabel.text = "$" + NumberFormatter.localizedString(from: Int((user.networth)!)! as NSNumber, number: NumberFormatter.Style.decimal)
self.avatarImageView.downloadImage(from: user.avatar!)
circularImage(photoImageView: self.avatarImageView)
}
} catch let error {
print(error)
}
}
task.resume()
Two suggestions:
strictly speaking, all accesses to UIView object should be on the main thread. You're dispatching to the main thread to call reloadView, but should probably also do it when you're settings the "username" and "net worth" values on the labels
are you sure that the labels are blank? Could it be an autolayout problem instead? (Try setting the background colour of the labels to yellow, to check that they're the correct size. Sometimes autolayout can squash views down to nothing if there are conflicting constraints)

Trying to append JSON items to array but not working

I am trying to serialize a GET request then make a movie object, then appending that movie object to a movies array which I will use to show info on the UI.
I am new and have struggled with this problem for some time now :(
If you look at the self.movies?.append(movie) shouldnt that work? I dont see any reasons as to when i try to get the first item i get fatal error index out of bounds which means I the Array is not filled yet.... Dont know what i am doing wrong :(
import UIKit
class ViewController: UIViewController {
var movies:[Movie]? = []
#IBOutlet weak var uiMovieTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
getMovieData()
print(self.movies?.count)
setUI()
}
#IBAction func yesBtn(_ sender: UIButton) {
print(movies?[5].title ?? String())
}
#IBAction func seenBtn(_ sender: UIButton) {
}
#IBAction func noBtn(_ sender: UIButton) {
}
#IBOutlet weak var moviePoster: UIImageView!
let urlString = "https://api.themoviedb.org/3/discover/movie?api_key=935f539acbfed4b9e5534ddeed3fb57e&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1&with_genres=12"
func getMovieData(){
//Set up URL
let todoEndPoint: String = "https://api.themoviedb.org/3/discover/movie?api_key=935f539acbfed4b9e5534ddeed3fb57e&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1&with_genres=12"
guard let url = URL(string: todoEndPoint) else {
print("Cant get URL")
return
}
let urlRequest = URLRequest(url: url)
//Setting up session
let config = URLSessionConfiguration.default
let session = URLSession.shared
//Task setup
let task = session.dataTask(with: urlRequest) { (data, URLResponse, error) in
//Checking for errors
guard error == nil else{
print("Error calling GET")
print(error)
return
}
//Checking if we got data
guard let responseData = data else{
print("Error: No data")
return
}
self.movies = [Movie]()
do{//If we got data, if not print error
guard let todo = try JSONSerialization.jsonObject(with: responseData, options:.mutableContainers) as? [String:AnyObject] else{
print("Error trying to convert data to JSON")
return
}//if data is Serializable, do this
if let movieResults = todo["results"] as? [[String: AnyObject]]{
//For each movieobject inside of movieresult try to make a movie object
for moviesFromJson in movieResults{
let movie = Movie()
//If all this works, set variables
if let title = moviesFromJson["title"] as? String, let movieRelease = moviesFromJson["release_date"] as? String, let posterPath = moviesFromJson["poster_path"] as? String, let movieId = moviesFromJson["id"] as? Int{
movie.title = title
movie.movieRelease = movieRelease
movie.posterPath = posterPath
movie.movieId = movieId
}
self.movies?.append(movie)
}
}
}//do end
catch{
print(error)
}
}
////Do Stuff
task.resume()
}
func setUI(){
//uiMovieTitle.text = self.movies![0].title
//print(self.movies?[0].title)
}
}
my Movie class:
import UIKit
class Movie: NSObject {
var title:String?
var movieRelease: String?
var posterPath:String?
var movieId:Int?
var movieGenre:[Int] = []
//public init(title:String, movieRelease:String, posterPath:String,movieId:Int) {
// self.movieId = movieId
//self.title = title
//self.movieRelease = movieRelease
//self.posterPath = posterPath
//self.movieGenre = [movieGenre]
//}
}
getMovieData calls the network asynchronously. Your viewDidLoad invokes this, then calls setUI() - but the networking is still ongoing when setUI is called.
Instead, call setUI when the networking is complete - after the self.movies?.append(movie) line. The UI code will need to happen on the main thread. So...
for moviesFromJson... // your existing code
...
self.movies?.append(movie)
}
// Refresh UI now movies have loaded.
DispatchQueue.main.async {
setUI()
}
import UIKit
class ViewController: UIViewController {
var movies:[Movie]? = []
#IBOutlet weak var uiMovieTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
getMovieDataCall(completionHandler: {data, error in self. getMovieDataCallBack(data: data, error: error)})
}
func getMovieDataCallBack(data: Data?, error: Error?) {
if error == nil {
let dictionary = try! JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! Dictionary<String, AnyObject>
//do your appending here and then call setUI()
print("dictionaryMovie \(dictionary)")
} else {
showAlertView("", error?.localizedDescription)
}
}
func getMovieDataCall(completionHandler: #escaping (Data?, Error?) -> Void)){
//Set up URL
let todoEndPoint: String = "https://api.themoviedb.org/3/discover/movie?api_key=935f539acbfed4b9e5534ddeed3fb57e&language=en-US&sort_by=popularity.desc&include_adult=false&include_video=false&page=1&with_genres=12"
guard let url = URL(string: todoEndPoint) else {
print("Cant get URL")
return
}
let urlRequest = URLRequest(url: url)
//Setting up session
let config = URLSessionConfiguration.default
let session = URLSession.shared
//Task setup
let task = session.dataTask(with: urlRequest) { (data, URLResponse, error) in
if error != nil {
NSLog("GET-ERROR", "=\(error)");
completionHandler(nil, error)
} else {
let dataString = String(data: data!, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
print(dataString!)
completionHandler(data, nil)
}
task.resume()
}
func setUI(){
}

cannot use optional chaining on non-optional value of type 'Any' SWIFT

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.

How can I do an HTTP Post before reading the JSON?

I am reading JSON from a URL and that has been working correctly. This is my code:
#IBOutlet weak var ProfilesCell: UITableView!
let cellspacing: CGFloat = 50
var names = [String]()
var posts = [String]()
var locations = [String]()
var votes = [String]()
var comments = [String]()
override func viewDidLoad() {
super.viewDidLoad()
ProfilesCell.dataSource = self
let url:URL = URL(string: "http://"+Connection_String+":8000/profile_view")!
URLSession.shared.dataTask(with:url, completionHandler: {(data, response, error) in
if error != nil {
print(error)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
if let Profile = parsedData["Profile"] as! [AnyObject]?
{
for Stream in Profile {
if let fullname = Stream["fullname"] as? String {
self.names.append(fullname)
}
if let post = Stream["post"] as? String {
self.posts.append(post)
}
if let location = Stream["location"] as? String {
self.locations.append(location)
}
if let vote = Stream["votes"] as? String {
self.votes.append(vote.appending(" Votes"))
}
if let comment = Stream["comments"] as? String {
self.comments.append(comment.appending(" Comments"))
}
DispatchQueue.main.async {
self.ProfilesCell.reloadData()
}
}
}
} catch let error as NSError {
print(error)
}
}
}).resume()
}
That code above correctly parses the JSON and the data is returned to the TableView. I now want to do an HTTP Post before reading that JSON and the parameter name is profile_id and I know that is something wrong in my code because if I do an HTML form with the parameter, things work correctly.
This is the new code that I now have:
#IBOutlet weak var ProfilesCell: UITableView!
let cellspacing: CGFloat = 50
var names = [String]()
var posts = [String]()
var locations = [String]()
var votes = [String]()
var comments = [String]()
override func viewDidLoad() {
super.viewDidLoad()
ProfilesCell.dataSource = self
let url:URL = URL(string: "http://"+Connection_String+":8000/profile_view")!
let ss = "32"
var request = URLRequest(url:url)
let paramString = "profile_id=\(ss)"
request.httpMethod = "POST"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
request.httpBody = paramString.data(using: .utf8)
URLSession.shared.dataTask(with:url, completionHandler: {(data, response, error) in
if error != nil {
print(error)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
if let Profile = parsedData["Profile"] as! [AnyObject]?
{
for Stream in Profile {
if let fullname = Stream["fullname"] as? String {
self.names.append(fullname)
}
if let post = Stream["post"] as? String {
self.posts.append(post)
}
if let location = Stream["location"] as? String {
self.locations.append(location)
}
if let vote = Stream["votes"] as? String {
self.votes.append(vote.appending(" Votes"))
}
if let comment = Stream["comments"] as? String {
self.comments.append(comment.appending(" Comments"))
}
DispatchQueue.main.async {
self.ProfilesCell.reloadData()
}
}
}
} catch let error as NSError {
print(error)
}
}
}).resume()
}
Now with this extra code the URL is still being hit but profile_id is showing null even though I have hardcoded the number 32. I also get this message displayed:
Error Domain=NSCocoaErrorDomain Code=3840 "No value." UserInfo={NSDebugDescription=No value.}

Resources