Swift 3: Preload data before putting it into UITableView - ios

I have an app that will fetch exactly 100 strings from an API and place them into a UITableView. I wish to first preload the data into an array and then, once the array is fully populated with the 100 entries, load the data into the table.
Due to the asynchronous API call, it seems like I am unable to load data into the array before the table view starts populating its cells. Mainly, I am having difficulty getting the data out of the closure in the first place.
This is the API call defined in an APIAgent class:
func getAPIData(_ requestType: String, completionHandler: #escaping (Data) -> ()) {
let requestURL: URL = URL(string : baseURL + requestType)!
let currentSession = URLSession.shared
let task = currentSession.dataTask(with: requestURL) { (data, response, error) in
completionHandler(data!)
}
task.resume()
}
This is how the UITableView uses it:
protocol AsyncHelper {
func getData(data: Any)
}
class TableViewController: UITableViewController, AsyncHelper {
var dataEntries: [String] = []
func getData(data: Data) {
let entry: String = String(describing: data)
dataEntries.append(entry)
}
override func viewDidLoad() {
super.viewDidLoad()
for i in 1...100 {
apiAgent.getAPIData("entry" + String(i), entry: { entry in
self.getData(data: entry)
})
}
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "EntryCell", for: indexPath) as! EntryCell
let entry: String = dataEntries[indexPath.row] // index out of range error
DispatchQueue.main.async {
// add Strings to cell here
}
return cell
}
}
So it appears that the cells are being generated before data gets populated into the dataEntries array. How do I prevent the UITableView from generating the cells until dataEntries is populated.

If you are going to use a closure you won't need a protocol. You could change your networking code to:
var songData = [Data]
func getAPIData(_ requestType: String, completionHandler: #escaping ([Data]) -> ()) {
let requestURL: URL = URL(string : baseURL + requestType)!
let currentSession = URLSession.shared
let task = currentSession.dataTask(with: requestURL) { (data, response, error) in
songData.append(data!)
if (songData.count == 100) {
completionHandler(songData)
}
}
task.resume()
}
This will make sure that your getData() and tableView.reloadData() will only be called once all 100 of your data elements have been loaded.
FYI - tableView.reloadData() will reload pretty much everything that has to deal with your table view. Your numberOfRows, numberOfSections, and cellForRow will all be called again. This will create the tableView over again using the updated dataEntries values

Try this :
override func viewDidLoad() {
super.viewDidLoad()
tblView.delegate = nil
tblView.dataSource = nil
for i in 1...100 {
apiAgent.getAPIData("entry" + String(i), entry: { entry in
self.getData(data: entry)
tblView.delegate = self
tblView.dataSource = self
tblView.reloadData()
})
}
}

Related

How to send Json Data to Table View Array? Swift

I've been researching and wrecking my brain attempting to get my JSON data to load into my tableview. I've tried placing the data in a Variable & I'm able to see the data in the console when I print it, however unable to push it to my table view.
Am I doing something wrong on the data page or am I not properly accessing the data within the loop?
I've tried putting the loop in the viewdidload but haven't been successful either.
// ViewController
import Foundation
import UIKit
import SDWebImage
class EntertainmentViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var A = EntertainmentApi()
var data = [EntertainmentPageData]()
var AA = EntertainmentApi().userFeedPosts
#IBOutlet weak var entPostTableView: UITableView!
override func viewDidLoad() {
func showTable() {
}
entPostTableView.register(EntertainmentViewrTableViewCell.nib(), forCellReuseIdentifier: EntertainmentViewrTableViewCell.identifier)
entPostTableView.delegate = self
entPostTableView.dataSource = self
super.viewDidLoad()
DispatchQueue.main.async {
self.entPostTableView.reloadData()
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customCell1 = tableView.dequeueReusableCell(withIdentifier: EntertainmentViewrTableViewCell.identifier, for: indexPath) as! EntertainmentViewrTableViewCell
customCell1.profileDisplayName.text = AA[indexPath.row].postDisplayName
self.AA.forEach({ (EntertainmentPageData) in
customCell1.configue(with: EntertainmentPageData.postDisplayName, PostImage: EntertainmentPageData.imageURLString, PostDescription: EntertainmentPageData.postDescription)
})
return customCell1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
func item(for index: Int) -> EntertainmentPageData {
return data[index]
}
func numberOfItems() -> Int {
return data.count
}
}
//Data
import SwiftUI
import SDWebImage
public protocol EntertainmentPagePostItem {
/// The image for the card.
var imageURLString: String { get }
/// Rating from 0 to 5. If set to nil, rating view will not be displayed for the card.
var postDescription: String? { get }
/// Will be displayed in the title view below the card.
var postDisplayName: String { get }
}
public protocol EntertainmentPagePostDataSource: class {
/// CardSliderItem for the card at given index, counting from the top.
func item(for index: Int) -> EntertainmentPagePostItem
/// Total number of cards.
func numberOfItems() -> Int
}
struct HomePagePost: Codable {
var displayName: String
var cityStatus: String
var displayDescription: String
var displayImageURL: String
var lookingFor: String
var profileImager1: String?
var profileImager2: String?
var profileImager3: String?
var profileImager4: String?
}
struct EntertainmentPageData: Codable {
let postDisplayName: String
let imageURLString: String
let postDescription: String?
}
public class entPostFly: Codable {
let postDisplayName, imageURLString, postDescription: String
}
struct eItem: EntertainmentPagePostItem {
var postDisplayName: String
var imageURLString: String
var postDescription: String?
}
public class EntertainmentApi {
var userFeedPosts = [EntertainmentPageData]()
init() {
load()
}
func load() {
guard let apiURL = URL(string: "https://api.quickques.com/....") else {
return
}
let task: () = URLSession.shared.dataTask(with: apiURL) { Data, apiResponse, error in
guard let Data = Data else { return }
do {
let entPostData = try JSONDecoder().decode([EntertainmentPageData].self, from: Data)
self.userFeedPosts = entPostData
}
catch {
let error = error
print(error.localizedDescription)
}
}.resume()
}
func getFeedPosts(completion: #escaping ([EntertainmentPageData]) -> () ) {
guard let apiURL = URL(string: "https://api.quickques.com/....") else {
return
}
let task: () = URLSession.shared.dataTask(with: apiURL) { Data, apiResponse, error in
guard let Data = Data else { return }
do {
let entPostData = try JSONDecoder().decode([EntertainmentPageData].self, from: Data)
completion(entPostData)
}
catch {
let error = error
print(error.localizedDescription)
}
}.resume()
}
}
class Api {
func getHomePagePosts(completion: #escaping ([HomePagePost]) -> Void ) {
guard let apiURL = URL(string: "https://api.quickques.com/.....") else {
return
}
let task: () = URLSession.shared.dataTask(with: apiURL) { Data, apiResponse, error in
guard let Data = Data else { return }
do {
let homePostData = try JSONDecoder().decode([HomePagePost].self, from: Data)
completion(homePostData)
}
catch {
let error = error
print(error.localizedDescription)
}
}.resume()
}
func getImageData(from url: URL, completion: #escaping (Data?, URLResponse?, Error?) -> ()) {
URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}
}
func getTopMostViewController() -> UIViewController? {
var topMostViewController = UIApplication.shared.keyWindow?.rootViewController
while let presentedViewController = topMostViewController?.presentedViewController {
topMostViewController = presentedViewController
}
return topMostViewController
}
First you have an empty function showTable inside your viewDidLoad - This does nothing. Presumably it is something hanging around from your various attempts. Delete that.
As you have probably worked out, your network fetch operation is going to occur asynchronously and you need to reload the table view once the data has been fetched.
You have some code in viewDidLoad that kind of tries to do this, but it isn't related to the fetch operation. It is just dispatched asynchronously on the next run loop cycle; This is probably still before the data has been fetched.
However, even if the data has been fetched, it won't show up because you are assigning userFeedPosts from a second instance of your API object to AA at initialisation time. This array is empty and will remain empty since Swift arrays are value types, not reference types. When userFeedPosts is updated, AA will hold the original empty array.
To load the data you need to
Start a load operation when the view loads
Pass a completion handler to that load operation to be invoked when the load is complete
Reload your table view with the new data
class EntertainmentViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var data = [EntertainmentPageData]()
#IBOutlet weak var entPostTableView: UITableView!
override func viewDidLoad() {
entPostTableView.register(EntertainmentViewrTableViewCell.nib(), forCellReuseIdentifier: EntertainmentViewrTableViewCell.identifier)
entPostTableView.delegate = self
entPostTableView.dataSource = self
super.viewDidLoad()
EntertainmentAPI.getFeedPosts { result in
DispatchQueue.main.async { // Ensure UI updates on main queue
switch result {
case .error(let error):
print("There was an error: \(error)")
case .success(let data):
self.data = data
self.entPostTableView.reloadData
}
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customCell1 = tableView.dequeueReusableCell(withIdentifier: EntertainmentViewrTableViewCell.identifier, for: indexPath) as! EntertainmentViewrTableViewCell
let post = data[indexPath.row)
customCell1.profileDisplayName.text = data[indexPath.row].postDisplayName
customCell1.configure(with: post.postDisplayName, PostImage: post.imageURLString, PostDescription: post.postDescription)
return customCell1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
}
public class EntertainmentAPI {
static func getFeedPosts(completion: #escaping ((Result<[EntertainmentPageData],Error>) -> Void) ) {
guard let apiURL = URL(string: "https://api.quickques.com/....") else {
return
}
let task = URLSession.shared.dataTask(with: apiURL) { data, apiResponse, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
/// TODO - Invoke the completion handler with a .failure case
return
}
do {
let entPostData = try JSONDecoder().decode([EntertainmentPageData].self, from: Data)
completion(.success(entPostData))
}
catch {
completion(.failure(error))
}
}.resume()
}
}

Swift 5 remote image loads in UITableViewCell on click not on load

I have a tableview cell that does not load properly. when it loads it is supposed to load the title and the image at the same time, like any normal page. as of now it only loads the images when you click on the cell. and when you click the image it is larger than it's supposed to be. not sure why. it's similar code used in another part of the project and I don't have that issue there. so there it is. not sure if what's going on is related to all those constraints I haven't set yet.
the project is an Amazon clone. the data is called from an api and the images are given as url strings. so I'm loading images from urls and placing them into my image views.
here's a video of what is going on. in the video I wait a few seconds before clicking on the cell to give it a chance to load on its own. in the code where the image is processed from the url there is a print statement that fires around the same time the title is generated so I know the image is created. it's just waiting until it is clicked to appear. not sure why. https://gfycat.com/talkativebackhornet
the page where the code is loaded. this code performs a search and uses the results to form the ProductsDetails struct where the data is displayed. this is basically just the Amazon page you get when you select a product. the image view will later be converted into a horizontal scrolling view once the code is working.
class ProductViewController: UITableViewController {
var asinForSearch: String = ""
var resultsManager = ResultsManager()
var productsDetails = Array<Products>()
{
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
resultsManager.detailsDelegate = self
tableView.dataSource = self
tableView.register(UINib(nibName: "ImagesCell", bundle: nil), forCellReuseIdentifier: "imagesCell")
tableView.rowHeight = 750
populateDetailsPage()
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return productsDetails.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let productResults = productsDetails[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "imagesCell", for: indexPath) as! ImagesCell
let url = URL(string: productResults.mainImage)
cell.imagioView.kf.setImage(with: url)
cell.title.text = productResults.title
return cell
}
func populateDetailsPage(){
resultsManager.getDetails(asinForSearch)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
extension ProductViewController: ResultsDetailDelegate {
func updateDetails(_ resultsManager: ResultsManager, products: Products){
self.productsDetails.append(products)
}
}
here's the code that creates the cell. not sure if this is necessary.
class ImagesCell: UITableViewCell, UIScrollViewDelegate {
#IBOutlet weak var imageScrollView: UIScrollView!
#IBOutlet weak var title: UILabel!
#IBOutlet weak var imagioView: UIImageView!
#IBOutlet weak var view: UIView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
imageScrollView.delegate = self
imageScrollView.contentSize = CGSize(width: imagioView.frame.width, height: imagioView.frame.height)
self.imageScrollView.addSubview(imagioView)
view.addSubview(imageScrollView)
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
}
func getDetails(_ asin: String){
let search = "\(detailRequest)\(asin)&country=US"
var request = URLRequest(url: URL(string: search)!)
request.httpMethod = "GET"
request.allHTTPHeaderFields = params
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
let task = session.dataTask(with: request) { data, response, error -> Void in
if let jsonData = data {
do {
let root = try JSONDecoder().decode(ProductDetail.self, from: jsonData)
if let productDetails = self.parseJSONDetails(root.product) {
self.detailsDelegate?.updateDetails(self, products: productDetails)
}
} catch {
print(error)
}
}
}
task.resume()
}
func parseJSONDetails(_ safeData: Products) -> Products? {
do {
let products = Products(asin: safeData.asin, deliveryMessage: safeData.deliveryMessage, productDescription: safeData.productDescription, featureBullets: safeData.featureBullets, images: safeData.images, mainImage: safeData.mainImage, price: safeData.price, productInformation: safeData.productInformation, title: safeData.title, totalImages: safeData.totalImages, totalVideos: safeData.totalVideos, url: safeData.url)
return products
} catch {
print()
}
}
let me know if there's any other code you need to see
You need to let your tableView know that there is new data and it needs to update its cells.
Call self.tableView.reloadData() when the delegate announces that it finished getting products.
extension ProductViewController: ResultsDetailDelegate {
func updateDetails(_ resultsManager: ResultsManager, products: Products){
self.productsDetails.append(products)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
Remove the reloading from here:
func populateDetailsPage(){
resultsManager.getDetails(asinForSearch)
}
This whole function is useless. It doesn't do anything:
func parseJSONDetails(_ safeData: Products) -> Products? {
do {
let products = Products(asin: safeData.asin, deliveryMessage: safeData.deliveryMessage, productDescription: safeData.productDescription, featureBullets: safeData.featureBullets, images: safeData.images, mainImage: safeData.mainImage, price: safeData.price, productInformation: safeData.productInformation, title: safeData.title, totalImages: safeData.totalImages, totalVideos: safeData.totalVideos, url: safeData.url)
return products
} catch {
print()
}
}
There is no reason why you would instantiate a Products object from another Products. At least, I can't understand why it is needed.
You have this ProductDetail object after you make the network request, then just pass its parameter .products to the delegate:
func getDetails(_ asin: String){
let search = "\(detailRequest)\(asin)&country=US"
var request = URLRequest(url: URL(string: search)!)
request.httpMethod = "GET"
request.allHTTPHeaderFields = params
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
let session = URLSession.shared
let task = session.dataTask(with: request) { [weak self] data, response, error -> Void in
guard let self = self else { return }
if let jsonData = data {
do {
let productDetail = try JSONDecoder().decode(ProductDetail.self, from: jsonData)
let products = productDetail.products
self.detailsDelegate?.updateDetails(self, products: products)
} catch {
print(error)
}
}
}
task.resume()
}
Also, make sure your detailsDelegate is declared as weak var, otherwise you risk creating a retain cycle.

How to Implement pagination in table view cell

My app retrieves json from the newsAPI.com . When I look at the json returned from the web service , it shows 2000 values however it returns 20 values loaded into my tableview controller. How do I increase the value so that when the user scrolls down the table view, they are presented with more values loaded into the table view controller?
class LatestNewsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
let newsData = Articles() //Model object
let urlRequest = "https://newsapi.org/v2/everything?q=coronavirus&apiKey=d32071cd286c4f6b9c689527fc195b03" //Website API
var urlSelected = ""
var articles: [Articles]? = [] // holds array of Articles data
var indexOfPageToRequest = 1
#IBOutlet weak var table_view: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
table_view.cellLayoutMarginsFollowReadableWidth = true
navigationController?.navigationBar.prefersLargeTitles = true
retriveData( )
}
func retriveData( )->Int{
guard let aritcleUrl = URL(string: urlRequest) else { //send a request to the server
return n
}
let request = URLRequest(url: aritcleUrl)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in //collects content from website
if error != nil { // checks if content is available
print(error ?? 0)
return
}
if let data = data { // converts data to an array of Article objects
self.articles = self.parseData(data: data)
}
})
task.resume()
return n
}
func parseData(data:Data)-> [Articles] {
var articles: [Articles]? = [] // holds parsed data
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
let jsonArticles = jsonResult?["articles"] as? [AnyObject] ?? [] // gets first head of json file and converts it to dictionary
for jsonArticle in jsonArticles{ // captures data and stores it in the model object
let article = Articles()
article.author = jsonArticle["author"] as? String
article.title = jsonArticle["description"] as? String
article.publishedAt = jsonArticle["publishedAt"] as? String
article.urlImage = jsonArticle["urlToImage"] as? String
article.urlWebsite = jsonArticle["url"] as? String
articles?.append(article) //put article data in the array
}
print(jsonArticles)
DispatchQueue.main.async {
if(articles!.count > 0)
{
self.table_view.reloadData()
}
}
} catch {
print("Nothing my guy\(error)")
}
return articles ?? [] // returns an array of articles
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return articles?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! NewsTableViewCell
cell.authorName.text = articles?[indexPath.row].author
cell.headLine.text = articles?[indexPath.row].title
cell.newsImage.downloadImage(from:(self.articles?[indexPath.item].urlImage ?? "nill"))
cell.timePublication.text = articles?[indexPath.row].publishedAt
return cell
}
[1]: https://i.stack.imgur.com/OY5G5.png
First, I would check the constraints of the table view, because this is a common issue (usually 0,0,0,0)
And also I would check the 'scrolling enabled' in the Attribute inspector and 'reuse Identifier'

SwiftyJSON and Alamofire data parsing in TableView (iOS, Swift 4)

When I run my project I am just able to get only tableView but datas are not fetched its blank. The struct codable and view controller codes were as follows. Please help me in viewing the datas in my tableView cells using alamofire and SwiftyJSON,
class Loads: Codable {
let loads: [Load]
init(loads: [Load]) {
self.loads = loads
}
}
class Load: Codable {
let id: String
let ad_title: String
let ad_description:String
let ad_price: String
let ad_post_date: String
let image1: String
init(ad_title: String, ad_description: String, ad_price: String, ad_post_date: String, image1: String) {
self.ad_title = ad_title
self.ad_description = ad_description
self.ad_price = ad_price
self.ad_post_date = ad_post_date
self.image1 = image1
}
}
View Controller codes:
import UIKit
import SwiftyJSON
import Alamofire
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var loads = [Load]()
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
downloadJson()
self.tableView.reloadData()
tableView.delegate = self
tableView.dataSource = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return loads.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "LoadCell") as? LoadCell else { return UITableViewCell() }
cell.labelA.text = loads[indexPath.row].ad_title
cell.labelB.text = loads[indexPath.row].ad_price
cell.labelC.text = loads[indexPath.row].ad_description
cell.labelD.text = loads[indexPath.row].ad_post_date
if let imageURL = URL(string: loads[indexPath.row].image1) {
DispatchQueue.global().async {
let data = try? Data(contentsOf: imageURL)
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
cell.loadImage.image = image
}
}
}
}
return cell
}
func downloadJson(){
Alamofire.request("https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON { response in
if let value = response.result.value {
let json = JSON(value)
//Printing strings from a JSON Dictionary
print(json[0]["ad_title"].stringValue)
print(json[0]["ad_price"].stringValue)
print(json[0]["ad_description"].stringValue)
print(json[0]["ad_post_date"].stringValue)
print(json[0]["image1"].stringValue)
}
self.tableView.reloadData()
}
self.tableView.reloadData()
}
}
I am using xcode9, swift 4.
datas are not fetched its blank
Your code does not seem to update var loads by downloaded data, that is why you just get blank table view. So, you need to assign fetched data to var loads.
Here is sample:
Alamofire.request("https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON { response in
// you should assign response data into var loads here
if let data = response.data {
do {
self.loads = try JSONDecoder().decode([Load].self, from: data)
} catch {
// exception
}
}
}
And after that, self.tableView.reloadData().
PS: Of course I don't know your response JSON format and your source code overall, so it might not be a directory answer to your question, but I hope it will help!
At first glance, the url looks faulty to me. https is typed twice, please remove once in below line.
Change this :
Alamofire.request("https:https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON
to :
Alamofire.request("https://alot.ae/api/classifiedcomputerlaptop.php").responseJSON
First off, your...
struct codable
... is not a struct at all, it is a class. change that to an actual struct.
Second, you start downloading before your delegate and datasource are set to the tableview.
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
downloadJson()
}
Thirdly, you are using Alamofire for a simple GET request, but you ignore the fact of asyncronous loading of images for a cell, which you do with Data. I'd suggest to use AlamoFireImageDownloader or remote AlamoFire all together. Using URLSession is just as easy:
private func request(url: URL, completionHandler: #escaping ((Data?, URLResponse?, Error?) -> Void)) {
var request = URLRequest(url: url)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request, completionHandler: completionHandler)
task.resume()
}
Fourth, I don't believe you need the loads codable.
Just an extra reminder, depending on the amount of Loads you'll have in your table, you will get problems with the loading of your images. The closures are not guaranteed to finish in order, but when they do they will update the cell regardless to whether the cell is already reused for another Load or not. Just FYI.

TableView is not loading any JSON data Swift 4

I have spent about three weeks trying to figure this out. I can get the section title to view, but none of the JSON data is showing. When I do a standard "array" contained in the file, it displays.
I have followed every tip and trick out there and I am stuck.
I think this may have something to do with AnyObject and String, but I am missing something. Please see my code below:
import UIKit
import Alamofire
import SwiftyJSON
class UserTableViewCell: UITableViewCell {
#IBOutlet weak var userFirstname: UILabel!
#IBOutlet weak var userLastname: UILabel!
}
class Profile2VC: UITableViewController {
#IBOutlet var userTable: UITableView!
var usertitles = ["First Name", "Last Name", "Email", "Mobile Number"]
var userinfo = [[String:AnyObject]]() //Array of dictionary
override func viewDidLoad() {
super.viewDidLoad()
let defaultValues = UserDefaults.standard
let URL_USER_LOGIN = "https://www.myapp.com/myphp.php"
let userid = "13"
let parameters: Parameters=["id":coolid]
Alamofire.request(URL_USER_LOGIN, method: .get, parameters:
parameters).responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
print(swiftyJsonVar)
if let userData = swiftyJsonVar["user"].arrayObject {
self.userinfo = userData as! [[String:AnyObject]]
//debugPrint(userData)
}
if self.userinfo.count > 0 {
self.userTable.reloadData()
}
}
}
self.userTable.reloadData()
// Uncomment the following line to preserve selection between
presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the
navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return userinfo.count
}
override func tableView(_ tableView: UITableView, titleForHeaderInSection
section: Int) -> String? {
return "Section \(section)"
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LabelCell",
for: indexPath) as! UserTableViewCell
//let userTitles = usertitles[indexPath.row]
let userInfo = userinfo[indexPath.row]
cell.userFirstname?.text = userInfo["first_name"] as? String
cell.userLastname?.text = userInfo["last_name"] as? String
//cell.imageView?.image = UIImage(named: fruitName)
//cell.textLabel?.text = usertitles[indexPath.row]
return cell
}
}
First of all you need to reload your table view in main queue. Check below code:
DispatchQueue.main.async {
self.userTable.reloadData()
}
And you are reloading it multiple times which is not good so Remove unwanted reload code and you final code will be:
Alamofire.request(URL_USER_LOGIN, method: .get, parameters: parameters).responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
print(swiftyJsonVar)
if let userData = swiftyJsonVar["user"].arrayObject {
self.userinfo = userData as! [[String:AnyObject]]
//debugPrint(userData)
}
if self.userinfo.count > 0 {
DispatchQueue.main.async {
self.userTable.reloadData()
}
}
}
}
//self.userTable.reloadData() //Remove this code
And once your API call done, Make sure debugPrint(userData) is printing some data and then when you are reloading userTable put a breakpoint in cellForRowAt and confirm that it's calling.
Then if its calling and data is there from server, You are good to go.
But if cellForRowAt method didn't called then you need to check your userTable DataSource and Delegate if it's correctly connected or not.
Try this code :
let API = URL(string:"http://www.myapp.com/myphp.php")
let request = URLRequest(url:API!)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) in
if let data = data {
if String(data: data, encoding: String.Encoding.utf8) != nil {
let data = data
let json = try? JSONSerialization.jsonObject(with: data, options: [])
let jsonData = json as! [[String:Any]]
DispatchQueue.main.sync {
let user = jsonData.flatMap { $0["user"] as? String }
print(user)
self.annocumentTableView.reloadData()
}
}
}
})
task.resume()

Resources