I am trying to get my JSON data to display in a tableview but cant get it to work. I have managed to display some data in text views but cannot get the data into the tableview
import UIKit
class ViewController: UIViewController, UITableViewDataSource {
final let url = URL(string: "http://**.***.**.**:*****/userconfig")
private var users = [User]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
downloadJSON()
}
func downloadJSON() {
guard let downloadURL = url else { return }
URLSession.shared.dataTask(with: downloadURL) { (data, urlResponse, error) in
guard let data = data, error == nil, urlResponse != nil else {
print("Trouble!")
return
}
print("Success!")
do {
let decoder = JSONDecoder()
let downloadedComms = try decoder.decode(Users.self, from: data)
self.users = downloadedComms.users
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print("Bad!")
}
}.resume()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return users.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "CommCell") as? CommCell else { return UITableViewCell() }
cell.nameLbl.text = users[indexPath.row].username
cell.DOBLbl.text = users[indexPath.row].online
return cell
}
}
Replace This with your viewDidLoad method. You didnt set your delegete and dataSource.
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
downloadJSON()
}
thanks for all your helpfull comments. It turns out that because I was not putting all the JSON fields in my class I needed to make the ones I was including optional.
class Users: Codable {
let users: [User]
init(users: [User]) {
self.users = users
}
}
class User: Codable {
let username: String?
let online: String?
let image: String?
init(username: String, online: String, image: String) {
self.username = username
self.online = online
self.image = image
}
Related
I'm kinda new to iOS, was working on network fetching from the GitHub API but not able to show the users in the table view. Below is the code,
View Controller:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var avatarImage: UIImageView!
#IBOutlet weak var userName: UILabel!
#IBOutlet weak var usersTableView: UITableView!
var network = Network()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
network.delegate = self
usersTableView.dataSource = self
}
override func viewWillAppear(_ animated: Bool) {
network.network()
}
}
extension ViewController: NetworkDelegate {
func updateTableView() {
DispatchQueue.main.async {
self.usersTableView.reloadData()
}
}
}
extension ViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if let users = network.users {
print(users.count)
return users.count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("CALLED")
let cell = tableView.dequeueReusableCell(withIdentifier: "userCell", for: indexPath) as! UserViewCell
return cell
}
}
btw, the identifier is from the .xib file, the identifier matches, I don't think the problem is occurring here.
Network File
import Foundation
protocol NetworkDelegate {
func updateTableView()
}
class Network {
var users: [GitHub]?
var delegate: NetworkDelegate?
func network() {
let url = "https://api.github.com/users"
let request: URLRequest?
if let URL = URL(string: url) {
request = URLRequest(url: URL)
URLSession.shared.dataTask(with: request!) { result, response, error in
if let data = result {
// print(String(data: data, encoding: .utf8)!)
self.users = self.parseJSON(data)
self.delegate?.updateTableView()
} else {
print(error!.localizedDescription)
}
}
.resume()
}
}
private func parseJSON(_ data: Data) -> [GitHub]? {
let json = JSONDecoder()
do {
let decodedData = try json.decode([GitHub].self, from: data)
// print(decodedData)
return decodedData
} catch {
print(error.localizedDescription)
}
return nil
}
}
The GitHub API Model
struct GitHub: Codable {
let login: String
let id: Int
let node_id: String
let avatar_url: String
let gravatar_id: String
let url: String
let html_url: String
let followers_url: String
let following_url: String
let gists_url: String
let starred_url: String
let subscriptions_url: String
let organizations_url: String
let repos_url: String
let events_url: String
let received_events_url: String
let type: String
let site_admin: Bool
}
When I run this code on the simulator, the output is blank (Below the label)
Not able to figure out where I'm doing wrong
Thanks In Advance.
Try to refactor you code using a completion handler without using the delegation pattern.
in your network file:
enum ApiError: Error {
case network(Error)
case genericError
case httpResponseError
case invalidData
case decoding
// you can handle your specific case
}
func network(completion: #escaping ( _ error: ApiError?, _ users: [GitHub]?)-> Void) {
let url = "https://api.github.com/users"
let request: URLRequest?
if let URL = URL(string: url) {
request = URLRequest(url: URL)
URLSession.shared.dataTask(with: request!) { result, response, error in
if let error = error {
completion(.network(error), nil)
return
}
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
completion( .httpResponseError, nil)
return
}
guard let data = result else {
completion(.invalidData, nil)
return
}
do {
let decodedData = try JSONDecoder().decode([GitHub].self, from: data)
completion(nil, decodedData)
} catch {
completion(.decoding, nil)
}
}
.resume()
}
}
then inside your ViewController you can use it this way:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
network.network { [weak self] error, users in
guard let self = self else { return }
if let error = error {
print(error)
return
}
DispatchQueue.main.async {
guard let users = users else { return }
self.users = users
self.tableView.reloadData()
}
}
}
if it still doesn't show and cellForRow doesn't get called, you probably have a problem with your constraints and the tableView frame is zero (either height, width or both).
Try to debug setting a breakpoint inside numberOfRowsInSection and then in your debug area po tableView or just print the tableView and check if width or height is zero. it will be probably get called a few times. The first time the frame should be zero but at some point you should get a frame with height and width. If don't then check your constraints.
You can check my example which has a table view 375 x 641
If your cell is a xib file then you have to register your cell with tableView.
before calling datasource in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
usersTableView(UINib(nibName: "userCell", bundle: nil), forCellReuseIdentifier: "userCell")
network.delegate = self
usersTableView.dataSource = self
}
I'm working with CocktailDB.
By creating a request I get a JSON file, parse it with Decodable protocol. From JSON I get all drinks' categories and display them as the sections of my tableview.
In each tableview section I want to display drinks from specific category (section's header). One drink per section cell from the category (drink's strDrink (name) and strDrinkThumb (image)).
I have a method that creates a request to get drinks from specific category - getDrinksFrom(category: String).
Please advice how can I call this method for specific section to get and display drinks from specific category in this section?
My code:
class ViewController: UIViewController {
var drinks = [Drink]()
var categories = [Category]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
getCategories()
getDrinksFrom(category: "Cocoa")
}
func getCategories() {
let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/list.php?c=list")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
self.categories = try JSONDecoder().decode(Categories.self, from: data!).drinks
DispatchQueue.main.async {
self.tableView.reloadData()
}
print(self.categories)
} catch {
print(error)
}
}
}.resume()
}
func getDrinksFrom(category: String) {
let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=\(category)")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
self.drinks = try JSONDecoder().decode(Drinks.self, from: data!).drinks
DispatchQueue.main.async {
self.tableView.reloadData()
}
print(self.drinks)
} catch {
print(error)
}
}
}.resume()
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return categories[section].strCategory
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "drinkCell") as! DrinkCell
cell.drinkName.text = drinks[indexPath.row].strDrink
let url = drinks[indexPath.row].strDrinkThumb
cell.drinkImage.downloaded(from: url)
return cell
}
}
// to download an image from web
extension UIImageView {
func downloaded(from url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
contentMode = mode
URLSession.shared.dataTask(with: url) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else { return }
DispatchQueue.main.async() { [weak self] in
self?.image = image
}
}.resume()
}
func downloaded(from link: String, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
guard let url = URL(string: link) else { return }
downloaded(from: url, contentMode: mode)
}
}
Category Model:
struct Categories:Decodable {
var drinks: [Category]
}
struct Category:Decodable {
var strCategory: String
}
Drink Model:
struct Drinks:Decodable {
var drinks: [Drink]
}
struct Drink:Decodable {
var strDrink: String
var strDrinkThumb: String
}
What I have for know:
JSON structure:
My suggestion is to create a custom struct Category with name and drinks for the sections. It does not conform to Decodable, this is intended
struct Category {
let name : String
var drinks : [Drink]
}
and an appropriate data source array
var categories = [Category]()
then load and parse the categories with traditional JSONSerialization and populate the array by mapping the names. Further add a completion handler
func getCategories(completion: #escaping () -> Void) {
let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/list.php?c=list")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let error = error { print(error); return }
do {
let result = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
let categoryNames = result["drinks"] as! [[String:String]]
self.categories = categoryNames.map{ Category(name: $0["strCategory"]!, drinks:[])}
completion()
} catch {
print(error)
}
}.resume()
}
To avoid naming confusion (too many drinks) name the root struct Response
struct Response : Decodable {
let drinks: [Drink]
}
Load the data related to a category and assign the drinks array to the corresponding array in categories
func getDrinksFrom(category: String) {
let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=\(category)")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let error = error { print(error); return }
do {
let drinks = try JSONDecoder().decode(Response.self, from: data!).drinks
guard let index = categories.firstIndex(where: {$0.name == category}) else { return }
self.categories[index].drinks = drinks
DispatchQueue.main.async {
self.tableView.reloadData()
}
} catch {
print(error)
}
}.resume()
}
and replace viewDidLoad with
override func viewDidLoad() {
super.viewDidLoad()
getCategories { [weak self] in
self?.getDrinksFrom(category: "Cocoa")
}
}
Finally change the table view data source methods to match the section structure
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return categories.count
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return categories[section].name
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return categories[section].drinks.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "drinkCell") as! DrinkCell
let category = categories[indexPath.section]
let drink = category.drinks[indexPath.row]
cell.drinkName.text = drink.strDrink
let url = drink.strDrinkThumb
cell.drinkImage.downloaded(from: url)
return cell
}
}
You can also put both functions together and load all drinks for all categories
func loadAllCategories() {
let url = URL(string: "https://www.thecocktaildb.com/api/json/v1/1/list.php?c=list")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if let error = error { print(error); return }
do {
let result = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
let categoryNames = (result["drinks"] as! [[String:String]]).map{$0["strCategory"]!}
let group = DispatchGroup()
for category in categoryNames {
let categoryURLString = "https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=\(category)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let categoryURL = URL(string: categoryURLString)!
group.enter()
let categoryTask = URLSession.shared.dataTask(with: categoryURL) { (categoryData, _, categoryError) in
defer { group.leave() }
if let categoryError = categoryError { print(categoryError); return }
do {
let drinks = try JSONDecoder().decode(Response.self, from: categoryData!).drinks
self.categories.append(Category(name: category, drinks: drinks))
} catch {
print(error)
}
}
categoryTask.resume()
}
group.notify(queue: .main) {
self.tableView.reloadData()
}
} catch {
print(error)
}
}.resume()
}
This is just a pseudocode, which will give you an idea how you can proceed further. The code has not been tested.
Create an array of sections to be loaded.
var sections: [Sections] = []
In you tableview delegates you can create a struct for the sections that you need to load, which will help you to identify the section in cell for row index path where you can call API based on categories.
extension ViewController: UITableViewDataSource, UITableViewDelegate {
struct Sections {
static var count = 0
// In stantiate table view headers index order
enum SectionType {
case SoftDrink
case OrdinaryDrink
case MilkShake
}
var type: SectionType?
var section: Int?
var rows: Int?
}
func setUpTableView() {
// Set Up Tableview Data
if check if Drink is type of SoftDrink /*If you sections are loaded dynamic u can add condition*/ {
sections.append(Sections(type: .SoftDrink, section: Sections.count, rows: 1))
Sections.count += 1
}
Sections.count = 0
}
func numberOfSections(in _: UITableView) -> Int {
sections.count
}
func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
sections[section].rows ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var tableCell: UITableViewCell = UITableViewCell()
guard let type = sections[indexPath.section].type else {
tableCell.selectionStyle = .none
return tableCell
}
switch type {
case .SoftDrink: break
// Instantiate cell and API calls.
case .OrdinaryDrink: break
// Instantiate cell and API calls.
case .MilkShake: break
// Instantiate cell and API calls.
}
tableCell.selectionStyle = .none
return tableCell
}
}
setUpTableView() can be called in viewDidLoad Method.
This is my controller that I am using to lookup the specific books. When I type in the search bar, no book information is displayed back to me while I type or after I finish typing. I would like to understand why and find a solution that would remedy this problem.
import UIKit
class TextbookSearchViewController: UIViewController, UITableViewDelegate {
#IBOutlet weak var searchBar: UISearchBar!
#IBOutlet weak var tableView: UITableView!
var booksFound = [[String: AnyObject]]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
}
func queryBooks(bookTitle: String) {
let stringURL = "https://www.googleapis.com/books/v1/volumes?q=\(bookTitle)"
guard let url = URL(string: stringURL) else {
print("Problem with URL")
return
}
let urlRequest = URLRequest(url: url as URL)
let urlSession = URLSession.shared
let queryTask = urlSession.dataTask(with: urlRequest) { (data, response, error) in
guard let jsonData = data else {
print("No Information could be Found:")
return
}
do {
let json = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String: AnyObject]
let tableItems = json["Items"] as! [[String: AnyObject]]
self.booksFound = tableItems
self.tableView.reloadData()
} catch {
print("Error with JSON: ")
}
}
queryTask.resume()
}
}
extension TextbookSearchViewController: UITableViewDataSource {
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return booksFound.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "BookCell", for: indexPath)
if let volumeInfo = self.booksFound[indexPath.row]["volumeInfo"] as? [String: AnyObject] {
cell.textLabel?.text = volumeInfo["title"] as? String
cell.detailTextLabel?.text = volumeInfo["subtitle"] as? String
}
return cell
}
}
extension TextbookSearchViewController: UISearchBarDelegate {
func searchBarButtonClicked(searchBar: UISearchBar) {
let bookTitle = searchBar.text?.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
self.queryBooks(bookTitle: bookTitle!)
searchBar.resignFirstResponder()
}
}
Probably you forget to set UISearchBar delegate
#IBOutlet weak var searchBar: UISearchBar! {
didSet {
searchBar.delegate = self
}
}
Also, you need below in place of func searchBarButtonClicked(searchBar: UISearchBar)
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
// your code
}
Key in response is items not Items
Use json["items"] in place of json["Items"]
Complete code:
import UIKit
class TextbookSearchViewController: UIViewController, UITableViewDelegate {
#IBOutlet weak var searchBar: UISearchBar! {
didSet {
searchBar.delegate = self
}
}
#IBOutlet weak var tableView: UITableView! {
didSet {
tableView.delegate = self
tableView.dataSource = self
}
}
var booksFound = [[String: AnyObject]]()
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
}
}
extension TextbookSearchViewController: UITableViewDataSource {
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return booksFound.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "BookCell", for: indexPath)
if let volumeInfo = self.booksFound[indexPath.row]["volumeInfo"] as? [String: AnyObject] {
cell.textLabel?.text = volumeInfo["title"] as? String
cell.detailTextLabel?.text = volumeInfo["subtitle"] as? String
}
return cell
}
}
extension TextbookSearchViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
let bookTitle = searchBar.text?.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
queryBooks(bookTitle: bookTitle!)
searchBar.resignFirstResponder()
}
func queryBooks(bookTitle: String) {
let stringURL = "https://www.googleapis.com/books/v1/volumes?q=\(bookTitle)"
guard let url = URL(string: stringURL) else {
print("Problem with URL")
return
}
let urlRequest = URLRequest(url: url as URL)
let urlSession = URLSession.shared
let queryTask = urlSession.dataTask(with: urlRequest) { [weak self] (data, response, error) in
guard let jsonData = data else {
print("No Information could be Found:")
return
}
do {
let json = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String: AnyObject]
guard let tableItems = json["items"] as? [[String: AnyObject]] else {
self?.booksFound = [[String: AnyObject]]()
return
}
print(tableItems)
self?.booksFound = tableItems
DispatchQueue.main.async {
self?.tableView.reloadData()
}
} catch {
print("Error with JSON: ")
}
}
queryTask.resume()
}
}
I already new in swift 3 and objetive c, right now I am stuck into how can I pass the id of each row to another table view controller when the user tap in the row the user want to go.
Here is the json data firstFile:
[
{"id_categoria":"1","totalRows":"323","nombre_categoria":"Cirug\u00eda"},
{"id_categoria":"2","totalRows":"312","nombre_categoria":"Med Interna"},
{"id_categoria":"3","totalRows":"6","nombre_categoria":"Anatomia"},
{"id_categoria":"4","totalRows":"24","nombre_categoria":"Anestesiologia"},
...]
Here is my first table view controller:
import UIKit
class CatMedVC: UIViewController, UITableViewDataSource {
#IBAction func volver(_ sender: Any) { }
#IBOutlet weak var listaCategoria: UITableView!
var fetchedCategoria = [Categoria]()
override func viewDidLoad() {
super.viewDidLoad()
listaCategoria.dataSource = self
parseData()
}
override var prefersStatusBarHidden: Bool{
return true
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fetchedCategoria.count
}
public func tableView(_ tableView: UITableView, cellForRowAt IndexPath: IndexPath) ->
UITableViewCell {
let cell = listaCategoria.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = fetchedCategoria[IndexPath.row].esp
cell?.detailTextLabel?.text = fetchedCategoria [IndexPath.row].totalRows
return cell!
}
func parseData() {
let url = "http://www.url.com/firstFile.php" //in json format
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { (data, response, error) in
if(error != nil) {
print("Error")
}
else {
do {
let fetchedData = try JSONSerialization.jsonObject(with:data!, options: .mutableLeaves) as! NSArray
//print(fetchedData)
for eachFetchedCategoria in fetchedData {
let eachCategoria = eachFetchedCategoria as! [String : Any]
let nombre_categoria = eachCategoria["nombre_categoria"] as! String
let totalRows = eachCategoria["totalRows"] as! String
let id_categoria = eachCategoria["id_categoria"] as! String
self.fetchedCategoria.append(Categoria(nombre_categoria: nombre_categoria, totalRows: totalRows, id_categoria: id_categoria))
}
//print(self.fetchedCategoria)
self.listaCategoria.reloadData()
}
catch {
print("Error 2")
}
}
}
task.resume()
}
}
class Categoria {
var nombre_categoria : String
var totalRows : String
var id_categoria : String
init(nombre_categoria : String, totalRows : String, id_categoria : String) {
self.nombre_categoria = nombre_categoria
self.totalRows = totalRows
self.id_categoria = id_categoria
}
}
So I need pass the id_categoria String into the another table view to show the data for the id selected previously...here I don't know how to do it...I have the json file waiting for the id selected previously..but I don't know how to catch it into the url
Here the second table view:
import UIKit
class EspMedVC: UITableViewController {
var TableData:Array< String > = Array < String >()
var EspecialidadArray = [String]()
#IBAction func volver(_ sender: Any) {
}
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url("http://www.url.com/secondFile.php?id=") // Here I need to put the id_categoria String in json format
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath)
cell.textLabel?.text = TableData[indexPath.row]
return cell
}
func get_data_from_url(_ link:String)
{
let url:URL = URL(string: link)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
self.extract_json(data!)
})
task.resume()
}
func extract_json(_ data: Data)
{
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch
{
return
}
guard let data_list = json as? NSArray else
{
return
}
if let nombre_especialidad = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let nombre_esp_obj = nombre_especialidad[i] as? NSDictionary
{
if let nombre_especialidad = nombre_esp_obj["subesp"] as? String
{
if let totalRows = nombre_esp_obj["totalRows"] as? String
{
TableData.append(nombre_especialidad + " [" + totalRows + "]")
}
}
}
}
}
DispatchQueue.main.async(execute: {self.do_table_refresh()})
}
func do_table_refresh()
{
self.tableView.reloadData()
}
}
This is a rough guide, please search for the methods in the documentation or here at other questions inside stackoverflow.
1) Add a variable inside your EspMedVC that will hold the "id_categoria String" that should be displayed.
2) Add a variable inside your CatMedVC that will hold the "id_categoria String" that the user selected.
3) Implement the "didSelectRow" delegate method from your tableview inside the "CatMedVC", inside this method you should set the variable set on step 2.
4) Implement the "prepareForSegue" method inside your CatMedVC, inside the the implementation you should retrieve the destination VC, cast it to "EspMedVC" and set the variable from step 1.
5) On the "viewDidLoad" from EspMedVC you can now use the variable set on step 2 to query your JSON and update the table view accordingly.
I am new to Swift, and am trying to create a table that reads JSON data from my website. I followed some tutorials, and was able to get my code to work with a table controller. Now I'm trying to use a view controller with a table view inside, so I can have more customization. My problem is, I can't get the data to actually show up when I try to use my new code.
This is what I have in my viewController.swift:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var TableData:Array< String > = Array < String >()
override func viewDidLoad() {
super.viewDidLoad()
get_data_from_url("http://www.stevenbunting.org/Alliris/service.php")
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = TableData[indexPath.row]
return cell
}
func get_data_from_url(_ link:String)
{
let url:URL = URL(string: link)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest, completionHandler: {
(
data, response, error) in
guard let _:Data = data, let _:URLResponse = response , error == nil else {
return
}
self.extract_json(data!)
})
task.resume()
}
func extract_json(_ data: Data)
{
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch
{
return
}
guard let data_list = json as? NSArray else
{
return
}
if let countries_list = json as? NSArray
{
for i in 0 ..< data_list.count
{
if let country_obj = countries_list[i] as? NSDictionary
{
if let country_name = country_obj["user"] as? String
{
if let country_code = country_obj["friendlist"] as? String
{
TableData.append(country_name + " [" + country_code + "]")
}
}
}
}
}
DispatchQueue.main.async(execute: {self.do_table_refresh()
})
}
func do_table_refresh()
{
self.tableView.reloadData()
}
}
Probably you didn't set the tableView's dataSource. To do this, implement the UITableViewDataSource-protocol in the ViewController-class and set the tableView's dataSource-property to self in the viewDidLoad(), for example:
class ViewController: UIViewController, UITableViewDataSource {
// ...
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
// ...
}
//...
}
Oh, and don't forget about the Apple Transport Security-settings, otherwise you won't see anything as iOS doesn't allow HTTP anymore, you have use HTTPS. The right way to handle this is to get an SSL-Certificate for your domain.
The quick'n'dirty and absolutely not recommended way is to disable ATS or to set an exception for certain, trustworthy domains.