Converting 3 seperate arrays of dictionaries into 1 struct array? - ios

I am currently using 3 separate arrays to populate a sectioned UITableView and I'd like to use just a single array for filtering purposes but still maintain the sections within my table.
The data is fed by a single JSON string which I then iterate through to create a dictionary of a single 'Event' and then based on the value of 'Event Status', it is added to one of three arrays (open, closed and deferred). These arrays are then uses to populate each section of a UITableView.
let myDictionary: [String: String] = [
"EventID" : item["EventID"] as! String,
"EventTitle" : item["EventTitle"] as! String,
"EventSummary" : item["EventSummary"] as! String,
"EventStatus" : item["Status"] as! String
]
if (item["Status"] as! String == "Open") {
self.openEvents.append(myDictionary as AnyObject)
}
if (item["Status"] as! String == "Closed") {
self.closedEvents.append(myDictionary as AnyObject)
}
if (item["Status"] as! String == "") {
self.deferredEvents.append(myDictionary as AnyObject)
}
Then in cellForRowAt:
var tmpDict: [String: String] = ["":""]
if sectionHeaders[indexPath.section] == "Open" {
tmpDict = openEvents[indexPath.row] as! [String : String]
}
if sectionHeaders[indexPath.section] == "Closed" {
tmpDict = closedEvents[indexPath.row] as! [String : String]
}
if sectionHeaders[indexPath.section] == "Deferred" {
tmpDict = deferredEvents[indexPath.row] as! [String : String]
}
Problem is that in order to allow filtering of this data, I'd have to run the filter against three different arrays which means three different 'filtered data' arrays. Which is far from ideal.
What I'd like to achieve is to have a single array of structured data (as opposed to dictionaries) that the user can filter through and the result returned to a single 'filtered data' array. I am doing this elsewhere with good effect, however, that is for a single list, not sectioned.
I have a struct setup and adding items to it as follows:
let data = EventItem(EventID: item["EventID"] as! String, EventTitle: item["EventTitle"] as! String, EventSummary: item["EventSummary"] as! String, EventStatus: item["Status"] as! String)
self.eventList.append(data)
and filtering using:
filteredData = eventList.filter() {
($0.EventTitle.lowercased() as NSString).contains(searchText.lowercased()) || ($0.EventSummary.lowercased() as NSString).contains(searchText.lowercased())
}
eventsTable.reloadData()
Is it possible to split this single array over different sections in a TableView? if so, how?

I would suggest giving a little more responsibility to your EventItem struct to help with the data source response. The you can then use a two dimensional array to manage both the section and row contents.
For example:
struct EventItem
{
var EventID = ""
var EventTitle = ""
var EventSummary = ""
var EventStatus = ""
func matches(_ searchText:String) -> Bool
{
return EventTitle.lowercased().contains(searchText.lowercased())
|| EventSummary.lowercased().contains(searchText.lowercased())
}
var section:Int
{
return EventStatus == "Opened" ? 0
: EventStatus == "Closed" ? 1
: 2
}
}
// you can maintain an "master" array of events (or build them from the dictionary as needed)
// I' using an array to keep the example simple
var eventList:[EventItem] = []
// This 2D array in your controller will serve as the store for your table view data source
var sectionData:[[EventItem]] = []
// applying filter is one line once you've added utility functions to your struct
sectionData = Array(0..<3).map{ section in return eventList.filter{$0.section == section && $0.matches(searchText)} }
// responding to tableview data source protocol only requires one line
// (which will not change even if you break down your data into more sections)
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{ return sectionData[section].count }
// getting to the data to populate the table view cell will be quite straightforward
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let rowData = sectionData[indexPath.section][indexPath.row]
// setup cell ...
}

You can just use an extension of the approach you already have for filteredData
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
switch section {
case 0:
let sortedList = newItems.sorted { $0.eventStatus == "Open"}
return sortedList.count
case 1:
let sortedList = newItems.sorted { $0.eventStatus == "Closed"}
return sortedList.count
case 2:
let sortedList = newItems.sorted { $0.eventStatus == ""}
return sortedList.count
default:
return 0 // shouldn't happen
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var tmpDict: [String: String] = ["":""]
switch section {
case 0:
tmpDict = newItems.sorted { $0.eventStatus == "Open"}
case 1:
tmpDict = newItems.sorted { $0.eventStatus == "Closed"}
case 2:
tmpDict = newItems.sorted { $0.eventStatus == ""}
default:
return 0 // shouldn't happen
// setup cell
}

Related

What am I doing wrong while populating this UITableView in Swift?

I am trying to populate a UITableView using an array and I am unable to do so. Here is what I have so far. This code is for retrieving data and storing it in the array that I am using to populate the UITableView:
func prepareForRetrieval() {
Database.database().reference().child("UserCart").child(Auth.auth().currentUser!.uid).observe(.value, with: {
(snapshot) in
for snap in snapshot.children.allObjects {
let id = snap as! DataSnapshot
self.keyArray.append(id.key)
}
self.updateCart()
})
}
func updateCart() {
for key in keyArray {
Database.database().reference().child("UserCart").child(Auth.auth().currentUser!.uid).child(key).observeSingleEvent(of: .value, with: {
(snapshot) in
let value = snapshot.value as? NSDictionary
let itemName = value?["Item Name"] as! String
let itemPrice = value?["Item Price"] as! Float
let itemQuantity = value?["Item Quantity"] as! Int
self.cartArray.append(CartData(itemName: itemName, itemQuantity: itemQuantity, itemPriceNumber: itemPrice))
print(self.cartArray.count)
})
}
}
The data is properly appending into the array and when I print the count of the array, it prints the correct count. This means that the data is there. However, when I try to populate a UITableView, it doesn't detect any data. I have the following code to make sure that there is data in the array before trying to populate the UITableView:
override func viewDidLoad() {
super.viewDidLoad()
cartBrain.prepareForRetrieval()
if cartBrain.cartArray.isEmpty == false{
tableViewOutlet.dataSource = self
tableViewOutlet.reloadData()
}
else {
tableViewOutlet.isHidden = true
tableViewOutlet.isUserInteractionEnabled = false
purchaseButtonOutlet.isEnabled = false
cartEmptyLabel.text = "Your cart is empty. Please add items and check back later."
}
}
When I open the View Controller, the TableView is disabled because it doesn't detect any data. I have already set the data source to self and the thing is that when the count of the array is printed, it again prints the correct amount. I have already set the data source to self for the UITableView. Here is my code for the UITableView:
extension CartViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cartBrain.cartArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cartcustomcell", for: indexPath)
cell.textLabel?.text = cartBrain.cartArray[indexPath.row].itemName
cell.detailTextLabel?.text = String(cartBrain.cartArray[indexPath.row].itemQuantity)
return cell
}
}
I don't understand why the count of the array prints the correct amount meaning that there is data stored in it but when the View Controller is loaded, it detects that the array is empty. Thanks for the help and I'm sorry if the question is a bit unclear.
After appending data to cartArray in updateCart you should reloadData(), like this:
weak var tableViewOutlet: UITableView?
func updateCart() {
for key in keyArray {
Database.database().reference().child("UserCart").child(Auth.auth().currentUser!.uid).child(key).observeSingleEvent(of: .value, with: {
(snapshot) in
let value = snapshot.value as? NSDictionary
let itemName = value?["Item Name"] as! String
let itemPrice = value?["Item Price"] as! Float
let itemQuantity = value?["Item Quantity"] as! Int
self.cartArray.append(CartData(itemName: itemName, itemQuantity: itemQuantity, itemPriceNumber: itemPrice))
DispatchQueue.main.async {
self.tableViewOutlet.reloadData()
}
})
}
}
The updateCart doesn't seem to have any connection to the tableViewOutlet so you need to pass in a reference to it in your viewDidLoad like this:
override func viewDidLoad() {
super.viewDidLoad()
cartBrain.tableViewOutlet = tableViewOutlet
cartBrain.prepareForRetrieval()
Note: Since you're using a for loop to trigger the async call multiple times you can use the array count to check if all the items are appended to do the reload to avoid multiple reloads.

Not able to sort table view data in ascending order

I have an table view which will populate some data. Now I need to sort my table view data in ascending order.
var SearchedobjectArray = [Objects]()
struct Objects {
var looId : String!
var looName : String
var looImageUrl:String!
var looCategoryType:String!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier:"cell", for: indexPath) as? MyLooCell{
cell.looImage.setShowActivityIndicator(true)
cell.looImage.setIndicatorStyle(.gray)
let imageURL = SearchedobjectArray[indexPath.row].looImageUrl
if (imageURL?.isEmpty)! {
let imageUrl = self.getDefaultImageForCategory(categoryName: SearchedobjectArray[indexPath.row].looCategoryType)
cell.looImage.image = UIImage(named: imageUrl)
} else {
cell.looImage.sd_setImage(with: URL(string: SearchedobjectArray[indexPath.row].looImageUrl))
}
cell.looName.text = SearchedobjectArray[indexPath.row].looName
let looCatType = SearchedobjectArray[indexPath.row].looCategoryType
} else {
return UITableViewCell()
}
}
I tried with : let array = SearchedobjectArray.sorted(by: )
But I am not sure how can I sort this data with ascending order a to z. I tried with other sorted() also but not able to achieve.
When data is fetched in an array then you can simply sort the array on looName basis using the following code.
SearchedobjectArray = SearchedobjectArray.sorted(by: { $0.looName > $1.looName})
tableView.reloadData()
You need to sort your array of objects and then tableView.reloadData(). Here's a Playground example of how to sort your array:
import Cocoa
struct Objects {
var looId : String!
var looName : String
var looImageUrl:String!
var looCategoryType:String!
}
var SearchedobjectArray = [Objects]()
let c = Objects(looId: "Chase", looName: "Chase", looImageUrl: "Chase", looCategoryType: "Chase")
SearchedobjectArray.append(c)
let b = Objects(looId: "Bree", looName: "Bree", looImageUrl: "Bree", looCategoryType: "Bree")
SearchedobjectArray.append(b)
let a = Objects(looId: "Adam", looName: "Adam", looImageUrl: "Adam", looCategoryType: "Adam")
SearchedobjectArray.append(a)
print("Before sorting")
print(SearchedobjectArray)
// The real sorting is happening here...I guessed you wanted to sort by looName
SearchedobjectArray = SearchedobjectArray.sorted(by: { $0.looName < $1.looName })
print("After sorting")
print(SearchedobjectArray)

Swift: How to make a new indexPath for the extra keys of a dictionary

I want the iteration of a dictionary to happen for all keys in it, not just for one in the indexPath.row
struct Whatever {
var title: String
var tag: [String:String?]?
}
var cases = [
Whatever(title: "Name1", tag: ["key1": "value1", "key2":"value2"]),
Whatever(title: "Name2", tag: ["key3": "value3"]
]
Later in the ViewController:
let arrayCases = cases[indexPath.row]
let caseTag = arrayCases.tag!
for key in caseTag.keys {
cell.titleLabel?.text = key
//the magic stops somewhere here
}
for value in caseTag.values {
if value != nil {
cell.txt.text = value
} else {
cell.txt.text = arrayCases.title
}
}
Could you tell me how to make a new indexPath.row for the second tag? As if it's a separate insurance of 'Whatever'?
Second question - why does it show after each build a different tag - sometimes it's "tag1", other times it's "tag2"?
Thank you!
I would add two calculated properties to your struct that returns a list of tag keys and values respectively to make the rest of the code cleaner.
var allTagKeys: String {
if let keys = tag?.keys {
return keys.sorted().joined(separator: ", ")
}
return ""
}
var allTagValues: String {
if let values = tag?.compactMap({ $0.value }) {
return values.joined(separator: ", ")
}
return ""
}
}
Note that I added sorting to the keys, not sure you want that.
If you use a standard table view (without sections) each item in the data source represents one row. You cannot simply make a new indexPath.row.
You have two options:
Use sections: One Whatever is one section, the title is the header, each tag is one row (see code below)
Concatenate the tag keys and values
cell.titleLabel?.text = caseTag.keys.joined(separator: ", ")
cell.txt.text = caseTag.values.joined(separator: ", ")
Regarding second question: Dictionaries are unordered, there is no order. If you need a specific order use another struct Tag and make tags an array for example
struct Whatever {
let title: String
let tags: [Tag]
}
struct Tag {
let key, value : String
}
let cases = [
Whatever(title: "Name1", tags: [Tag(key: "key1", value: "value1"), Tag(key: "key2", value: "value2")]),
Whatever(title: "Name2", tags: [Tag(key: "key3", value: "value3"), Tag(key: "key4", value: "value4")])
]
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let case = cases[section]
return case.title
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
let section = cases[indexPath.section]
let case = section.tags[indexPath.row]
cell.titleLabel?.text = case.key
cell.txt.text = case.value

Multiple Table View with different arrays

I have a detail view that shows the details of an event, the people who participate and the people who asked to participate. I have created two arrays of different types but they have the same fields, only that a first structure represents the users with the 'status_confirm' field equal to 1 (therefore Accepted Users), while the other has as 'status_confirm' equal to 0 (Users awaiting acceptance). I declared two arrays, the first one: var arrayUserAccepted = [User_accepted] ().
The second one: var arrayUserWaiting = [User_waiting] (). Struct Image
Next step: I populate these structures via a php script
func getData(){
let url = URL(string: “MYURL”)
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
guard let data = data, error == nil else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String:AnyObject]
print("JSON: \n\(json)\n")
let waiting = json["waiting"] as! [AnyObject]
let accepted = json["accepted"] as! [AnyObject]
DispatchQueue.main.async {
for list_user_waiting in waiting {
let id_user_waiting = list_user_waiting["id_user”] as! String
let name_user_waiting = list_user_waiting[“name_user”] as! String
let email_user_waiting = list_user_waiting["email"] as! String
var photo_user_waiting = list_user_waiting[“photo”]
let status_user_waiting = list_user_waiting["status”] as! String
if photo_user_waiting is NSNull {
photo_user_waiting = ""
}
let listUserWaiting = User_waiting(id_user_waiting: id_user_waiting, name_user_waiting: name_user_waiting, email_user_waiting: email_utente_attesa, foto_waiting: photo_user_waiting as! String, status_waiting: status_user_waiting)
self.arrayUserWaiting.append(listUserWaiting)
self.tableViewListUserWaiting.reloadData()
}
for list_user_accepted in accepted {
let id_user_accepted = list_user_accepted["id_utente"] as! String
let name_user_accepted = list_user_accepted["name_utente"] as! String
let email_user_accepted = list_user_accepted["email"] as! String
var photo_user_accepted = list_user_accepted[“photo"]
let status_user_accepted = list_user_accepted["status”] as! String
if photo_user_accepted is NSNull {
photo_user_accepted = ""
}
let listUserAccepted = User_accepted(id_user: id_user_accepted, nome_utente: name_user_accepted, email: email_user_accepted, foto: photo_user_accepted as! String, stato: status_user_accepted)
self.arrayUserAccepted.append(listUserAccepted)
self.tableViewListUserAccepted.reloadData()
}
}
} catch let error as NSError {
print(error)
}
}).resume()}
This above is a function that I call in the viewDidLoad(). The next step would be to use the functions of the table view and it is here that I think there is the injunction
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count: Int?
if tableView == self.tableViewListUserAccepted {
count = arrayUserAccepted.count
}
if tableView == self.tableViewListUserWaiting {
count = arrayUserWaiting.count
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
if tableView == self.tableViewListUserAccepted {
cell.imageProfileUserAccepted.image = UIImage(named: "imageDefault")
cell.valueSliderUserAccepted.value = Float(50) //JUST FOR POPULATE THE INTERFACE
cell.name_user_accepted.text = arrayUserAccepted[indexPath.row].name_user
}
if tableView == self.tableViewListUserWaiting {
cell.imageProfileUserWaiting.image = UIImage(named: "imageDefault")
cell.valueSliderUserWaiting.value = Float(23) //JUST FOR POPULATE THE INTERFACE
cell.name_user_waiting.text = arrayUserWaiting[indexPath.row].name_user_waiting
}
return cell
}
Once done all this round, I start the application but nothing. The tables are empty. In the console the script answers me correctly and so I can not figure out where the error could be. Needless to say, I have declared the .delegate and .dataSource of both tables, both in the Main.Storyboard and in the code.
Everything is fine just change the format of IF condition and it will work.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count: Int?
if tableView == self.tableViewListUserAccepted {
count = arrayUserAccepted.count
} else {
count = arrayUserWaiting.count
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableViewListUserAccepted {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.imageProfileUserAccepted.image = UIImage(named: "imageDefault")
cell.valueSliderUserAccepted.value = Float(50) //JUST FOR POPULATE THE INTERFACE
cell.name_user_accepted.text = arrayUserAccepted[indexPath.row].name_user
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.imageProfileUserWaiting.image = UIImage(named: "imageDefault")
cell.valueSliderUserWaiting.value = Float(23) //JUST FOR POPULATE THE INTERFACE
cell.name_user_waiting.text = arrayUserWaiting[indexPath.row].name_user_waiting
return cell
}
}
Also check if the datasource and delegate of both of your tableView are set. Finally call the tableView.reloadTable() method on both of your tableviews after you populate your arrays in the viewDidLoad() method.

How to return a cell in tableview if it is nested inside an if statement

This is my code —- I am getting error when returning cell1 inside the if statement as it says ” Cannot return a non void return value in void function.I want to return the cell in tableview .. and i have 3 kind of posts .. one for status one for image one for video post. How can i return the cell for each.
P.S. : I have just provided the code for one post type only as if one is solved then all other can be solved.
import UIKit
import Alamofire
class ViewController: UIViewController , UITableViewDelegate ,
UITableViewDataSource{
#IBOutlet weak var feedTable: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
feedTable.dataSource = self
feedTable.delegate = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 376
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
Alamofire.request("https://new.postpfgsdfdsgshfghjoves.com/api/posts/get_all_posts").responseJSON { response in
let result = response.result
if let dict = result.value as? Dictionary<String,AnyObject> {
if let successcode = dict["STATUS_CODE"] as? Int {
if successcode == 1 {
if let postsArray = dict["posts"] as? [Dictionary<String,AnyObject>]
{
for i in 0..<postsArray.count
{
let posttype = postsArray[i]["media_type"] as! String
if posttype == "image"
{
let cell1 : ImageTableViewCell = self.feedTable.dequeueReusableCell(withIdentifier: "imageReuse") as! ImageTableViewCell
cell1.fullName = postsArray[i]["full_name"] as? String
cell1.profileImageURL = postsArray[i]["profile_pic"] as? String
cell1.location = postsArray[i]["location"] as? String
cell1.title = postsArray[i]["title"] as? String
cell1.postTime = postsArray[i]["order_by_date"] as? String
cell1.likes = postsArray[i]["liked_count"] as? Int
cell1.comments = postsArray[i]["comment_count"] as? Int
cell1.imageURL = postsArray[i]["profile_pic"] as? String
cell1.imageLocation = postsArray[i]["location"] as? String
cell1.content = postsArray[i]["content"] as? String
cell1.profileFullName.text = cell1.fullName
cell1.titleImagePost.text = cell1.title
cell1.postLocation.text = cell1.location
cell1.profileUserLocation.text = cell1.location
cell1.numberOfLikes.text = "\(cell1.likes!) Likes"
cell1.numberOfComments.text = "\(cell1.comments!) Comments"
cell1.postTimeOutlet.text = postsArray[i]["posted_on"] as? String
let url = URL(string: cell1.imageURL!)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell1.profileImage.image = UIImage(data: data!)
let url1 = URL(string: cell1.imageURL!)
let data1 = try? Data(contentsOf: url1!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell1.postedImage.image = UIImage(data: data1!)
// return cell1
}
else if posttype == "status"
{
let cell1 : StatusTableViewCell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "statusReuse") as! StatusTableViewCell
cell1.fullName = postsArray[i]["full_name"] as? String
cell1.profileImageURL = postsArray[i]["profile_pic"] as? String
cell1.location = postsArray[i]["location"] as? String
cell1.title = postsArray[i]["title"] as? String
cell1.postTime = postsArray[i]["order_by_date"] as? String
cell1.likes = postsArray[i]["liked_count"] as? Int
cell1.comments = postsArray[i]["comment_count"] as? Int
cell1.postContent = postsArray[i]["content"] as? String
cell1.profileFullName.text = cell1.fullName
cell1.titleStatusPost.text = cell1.title
cell1.postLocation.text = cell1.location
cell1.profileUserLocation.text = cell1.location
cell1.content.text = cell1.postContent
cell1.numberOfLikes.text = "\(cell1.likes!) Likes"
cell1.numberOfComments.text = "\(cell1.comments!) Comments"
cell1.postTimeOutlet.text = "\(cell1.postTime!)"
let url = URL(string: cell1.profileImageURL!)
let data = try? Data(contentsOf: url!) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
cell1.profileImage.image = UIImage(data: data!)
// return cell1
}
else if posttype == "video"
{
let cell1 : VideoTableViewCell = self.feedTable.dequeueReusableCell(withIdentifier: "videoReuse") as! VideoTableViewCell
cell1.fullName = postsArray[i]["full_name"] as? String
// cell1.profession = postsArray[i]["profession"] as? String
cell1.profileImageURL = postsArray[i]["profile_pic"] as? String
cell1.location = postsArray[i]["location"] as? String
cell1.title = postsArray[i]["title"] as? String
cell1.postTime = postsArray[i]["order_by_date"] as? String
cell1.likes = postsArray[i]["liked_count"] as? Int
cell1.comments = postsArray[i]["comment_count"] as? Int
cell1.videoURL = postsArray[i]["profile_pic"] as? String
cell1.profileFullName.text = cell1.fullName
cell1.titleVideoPost.text = cell1.title
cell1.postLocation.text = cell1.location
cell1.profileUserLocation.text = cell1.location
// return cell1
}
}
}
}
}
}
}
}
}
My answer isn't any different from the others but let me be a little more specific. I'll use a generic example and you'll need to tailor this to your specific needs.
1) Define a model somewhere for your data such as:
class MyDataItem {
var name: String
var title: String
var location: String
init(name: String, title: String, location: String) {
self.name = name
self.title = title
self.location = location
}
}
2) Define an array in your Viewcontroller such as:
var dataArray = [MyDataItem]()
3) Load the data which you could do from the viewDidLoad method:
override func viewDidLoad() {
super.viewDidLoad()
feedTable.dataSource = self
feedTable.delegate = self
loadData()
}
4) Implement loadData() function:
func loadData() {
// Here put in your alamo enclosure to retrieve the data and store it into the array you've defined
// When done, call reload data
feedTable.reloadData()
}
5) Your cellForRowAt function will need to be modified to retrieve the data from the array. For example:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell1 : ImageTableViewCell = tableView.dequeueReusableCell(withIdentifier: "imageReuse") as! ImageTableViewCell
cell1.fullName = dataArray[indexPath.row].name
cell1.title = dataArray[indexPath.row].title
cell1.location = dataArray[indexPath.row].location
return cell1
}
Anyway, this is the general idea on how to do what you are attempting. When reloadData is called from your loadData function, it will cause the tableview to reload from the array data correctly.
Hope this helps!
The problem is you do not return the cell, you simply make some async request with alamofire and return an instance of the cell from the closure.
func foo() -> Int { return 1 } ≠ func bar() -> Int { someClosure { return 1 } }
Firstly you need load the the data from https://www.example.com/api/posts/get_all_posts into some data model.
var models: [SomeTypeYouCreate] = []
func loadData() {
Alamofire.request(...).responseJSON { response in
self.models = /* Create array of `SomeTypeYouCreate` objects from response */
self.tableView.reloadData()
}
}
func func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let model = self.models[indexPath.row]
// configure cell with model
return cell
}
You cannot do it the way you're trying to. You're not returning a cell from cellForRowAt method, you're returning it in Alamofire callback closure. What you should do is to return the cell in your cellForRowAt method, and implement some sort of setup method for your UITableViewCell subclass and make your calls in there
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell1 : ImageTableViewCell = self.feedTable.dequeueReusableCell(withIdentifier: "imageReuse") as! ImageTableViewCell
// put your Alamofire code inside such function in your UITableViewCell subclass
cell.setup()
return cell
}
First and foremost, you are returning value in closure Alamofire.request. If you wanna use cell after you confirm cell values, you want to pass over completion handler to the function and use it in that Alamofire.reqeust...
But if I were you, I would create another function which is called before/after tableView function.
If it is Before then trigger tableview initialization upon alamofire completion.
If it is After then reload when values are loaded correctly in Alamofire.
EDITED:
Like other suggested,it is bad idea to load data in tableView function. Also, by using Alamofire, it means you use Closure. That is, whatever you wanna do in Alamofire happens asynchronously, meaning by the time what you want to achieve in Alamofire is done, your program can be out of the table view function. Also, since it is closure, returning value in Alamofire does not satisfy your tableView return type.
So basically, if you need data via API and verify, you declare function such that do whatever you doing Alamofire and then reload the tableView.
So flow is like this:
1) Make an empty array and put array.count to # of rows.
2) Since it is empty, when tableView first try to generate cells, it doesn't do anything.
3) You call the function which uses Alamofire. If returned values are good, then add the cell(model) to the array.
4) After you are done loading models, do tableView.reload().
5) Tableview calls tableView function now it finds value in array so that will create cells.

Resources