Reputation: 47
I have a local json file ( you can find it here ).
I am trying to populate a UITableView
with this json and I can generate the table.
but I can only see the mentioned headers and not the respective data beneath the cells of those headers.
I will post some of the code below and can share more if it helps
enum ProfileViewModelItemType {
case nameAndPicture
case about
case email
case friend
case attribute
}
protocol ProfileViewModelItem {
var type: ProfileViewModelItemType { get }
var rowCount: Int { get }
var sectionTitle: String { get }
}
extension ProfileViewModelItem {
var rowCount: Int {
return 1
}
}
class ProfileViewModelNameItem: ProfileViewModelItem {
var type: ProfileViewModelItemType {
return .nameAndPicture
}
var sectionTitle: String {
return "Main Info"
}
}
class ProfileViewModelNameAndPictureItem: ProfileViewModelItem {
var type: ProfileViewModelItemType {
return .nameAndPicture
}
var sectionTitle: String {
return "Main Info"
}
var rowCount: Int {
return 1
}
var pictureUrl: String
var name: String
init(name: String , pictureUrl: String) {
self.name = name
self.pictureUrl = pictureUrl
}
}
class ProfileViewModelAboutItem: ProfileViewModelItem {
var type: ProfileViewModelItemType {
return .about
}
var sectionTitle: String {
return "About"
}
var rowCount: Int {
return 1
}
var about: String
init(about: String) {
self.about = about
}
}
class ProfileViewModelEmailItem: ProfileViewModelItem {
var type: ProfileViewModelItemType {
return .email
}
var sectionTitle: String {
return "Email"
}
var rowCount: Int {
return 1
}
var email: String
init(email: String) {
self.email = email
}
}
class ProfileViewModelAttributeItem: ProfileViewModelItem {
var type: ProfileViewModelItemType {
return .attribute
}
var sectionTitle: String {
return "Attributes"
}
var rowCount: Int {
return attributes.count
}
var attributes: [Attribute]
init(attributes: [Attribute]) {
self.attributes = attributes
}
}
class ProfileViewModeFriendsItem: ProfileViewModelItem {
var type: ProfileViewModelItemType {
return .friend
}
var sectionTitle: String {
return "Friends"
}
var rowCount: Int {
return friends.count
}
var friends: [Friend]
init(friends: [Friend]) {
self.friends = friends
}
}
class ProfileViewModel: NSObject {
var items = [ProfileViewModelItem]()
override init() {
super.init()
guard let data = dataFromFile("ServerData"), let profile = Profile(data: data) else {
return
}
if let name = profile.fullName, let pictureUrl = profile.pictureUrl {
let nameAndPictureItem = ProfileViewModelNameAndPictureItem(name: name, pictureUrl: pictureUrl)
items.append(nameAndPictureItem)
}
if let about = profile.about {
let aboutItem = ProfileViewModelAboutItem(about: about)
items.append(aboutItem)
}
if let email = profile.email {
let dobItem = ProfileViewModelEmailItem(email: email)
items.append(dobItem)
}
let attributes = profile.profileAttributes
// we only need attributes item if attributes not empty
if !attributes.isEmpty {
let attributesItem = ProfileViewModelAttributeItem(attributes: attributes)
items.append(attributesItem)
}
let friends = profile.friends
// we only need friends item if friends not empty
if !profile.friends.isEmpty {
let friendsItem = ProfileViewModeFriendsItem(friends: friends)
items.append(friendsItem)
}
}
}
extension ProfileViewModel:UITableViewDataSource {
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return items[section].sectionTitle
}
func numberOfSections(in tableView: UITableView) -> Int {
return items.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return items[section].rowCount
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let item = items[indexPath.section]
switch item.type {
case .nameAndPicture:
if let cell = tableView.dequeueReusableCell(withIdentifier: NameAndPictureCell.identifier, for: indexPath) as? NameAndPictureCell {
cell.item = item
return cell
}
case .about:
if let cell = tableView.dequeueReusableCell(withIdentifier: AboutCell.identifier, for: indexPath) as? AboutCell {
cell.item = item
return cell
}
case .email:
if let cell = tableView.dequeueReusableCell(withIdentifier: EmailCell.identifier, for: indexPath) as? EmailCell {
cell.item = item
return cell
}
case .friend:
if let item = item as? ProfileViewModeFriendsItem, let cell = tableView.dequeueReusableCell(withIdentifier: FriendCell.identifier, for: indexPath) as? FriendCell {
let friend = item.friends[indexPath.row]
cell.item = friend
return cell
}
case .attribute:
if let item = item as? ProfileViewModelAttributeItem, let cell = tableView.dequeueReusableCell(withIdentifier: AttributesCell.identifier, for: indexPath) as? AttributesCell {
cell.item = item.attributes[indexPath.row]
return cell
}
}
// return the default cell if none of above succeed
return UITableViewCell()
}
}
public func dataFromFile(_ filename: String) -> Data? {
@objc class TestClass: NSObject { }
let bundle = Bundle(for: TestClass.self)
if let path = bundle.path(forResource: filename, ofType: "json") {
return (try? Data(contentsOf: URL(fileURLWithPath: path)))
}
return nil
}
class Profile {
var fullName: String?
var pictureUrl: String?
var email: String?
var about: String?
var friends = [Friend]()
var profileAttributes = [Attribute]()
init?(data: Data) {
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let body = json["data"] as? [String: Any] {
self.fullName = body["fullName"] as? String
self.pictureUrl = body["pictureUrl"] as? String
self.about = body["about"] as? String
self.email = body["email"] as? String
if let friends = body["friends"] as? [[String: Any]] {
self.friends = friends.map { Friend(json: $0) }
}
if let profileAttributes = body["profileAttributes"] as? [[String: Any]] {
self.profileAttributes = profileAttributes.map { Attribute(json: $0) }
}
}
} catch {
print("Error deserializing JSON: \(error)")
return nil
}
}
}
class Friend {
var name: String?
var pictureUrl: String?
init(json: [String: Any]) {
self.name = json["name"] as? String
self.pictureUrl = json["pictureUrl"] as? String
}
}
class Attribute {
var key: String?
var value: String?
init(json: [String: Any]) {
self.key = json["key"] as? String
self.value = json["value"] as? String
}
}
the table view should not only have names of friends but the images also which are present in the assets .
I have also defined custom classes for all the different types of cell and dequeued them and registered them
been looking at it for a week now but still no, any help would be appreciated
I have also provided the frame and added all labels, images etc to the table view as subView but no luck
Upvotes: 0
Views: 80
Reputation: 2817
I had a bit of difficulty understanding. First, "i have also provided the frame and added all label, images etc to the table view as subview" sounds wrong. You should never need to call addSubview(...)
on a UITableView. You add UI elements to a UITableViewCell via tableCell.contentView.addSubview(...) but even then, you're best to design your table view cells in interface builder using autolayout. Setting frames is almost useless nowadays now that autolayout is here for good. The only place to set actual frames is in layoutSubviews()
Secondly, is it not displaying because you have your rowHeight to be a constant instead of UITableView.automaticDimension? Maybe you have cells designed to be a certain height, but you haven't told the table view that they have that height? I'd suggest googling "self-sizing table cells with autolayout" or using the table view delegate method ...heightForRowAt indexPath: ...
And finally, looking at your code, I would suggest modernizing your approach to JSON parsing:
Something like this, or just google "JSON and Codable swift"
Upvotes: 2