Deaconu Dan Andrei Deaconu Dan Andrei - 7 months ago 10
Swift Question

How can a UITableViewCell know what JSON to load from an Array or Dictionaries

Let's say I have an array of 3 dictionaries in JSON and each of those has it's own type (demo, entry, comment).

[
{
"_id": "random ID",
"profile": "random ID Profile",
"demo": {
"_id": "random ID",
"profile": {
"_id": "random ID",
"name": "name",
"username": "username",
"description": "description",
"picture": "/picture"
},
"path": "/path",
"created": "date"
},
"type": "demo",
"source": "570aa8f647a70780a7111e91",
"__v": 0,
"created": "date"
},
{
"_id": "random ID",
"comment": "random comment ID",
"type": "comment",
"source": "57077c4e356c10371ca91ad9",
"__v": 0,
"created": "date"
},
{
"_id": "random ID",
"entry": "random entry ID",
"type": "entry",
"source": "57077c4e356c10371ca91ad9",
"__v": 0,
"created": "date"
}
]


Right now I'm checking the type in the request so I only get the demo.

func getTimeline(urlString: NSURL, completion: ([ModelDemos]) -> Void) {
Alamofire.request(.GET, urlString).responseJSON { response in

if let httpResponse = response.response {
switch httpResponse.statusCode {
case 200:
var modelTimeline = [ModelDemos]()

if let demos = response.result.value as? [JSONDictionary] {
for demo in demos {
if let type = demo["type"] as? String {
if type == "demo" {
if let demo = demo["demo"] as? JSONDictionary {
let JSON = ModelDemos(data: demo)
modelTimeline.append(JSON)
}
}
}
}
} else { print("not working") }

dispatch_async(dispatch_get_main_queue()) {
completion(modelTimeline)
print("Am I back on the main thread ? Response: \(NSThread.isMainThread())")
}
default:
return
}
}
}
}


After this setting a completion method in my TimelineViewController

var timelineDemos = [ModelDemos]()

func runApiManagerTimeline() {
guard let urlString = urlString else {return}
apiManagerCurrentUserProfile.getTimeline(urlString, completion: didGetCurrentUserProfileDemos)
}

func didGetCurrentUserProfileDemos(demos: [ModelDemos]) {
timelineDemos = demos
timelineCollectionView.reloadData()
}


All works fine, I get only the demo Dictionary and I can load it to the DemoUITableViewCell.

Now I have to create 3 different types of UITableViewCell for each of the Dictionary from the array. Imagine it as a Facebook feed where each Dictionary is different and the number is continuously growing.

How would I tell each Cell what content it should load?

Answer

Just another good example to use a custom struct as model for the data source array.

First create an enum to map the String types to enum cases

enum DataType : String { case Demo = "demo", Comment = "comment", Entry = "entry" }

Create a custom struct item as data source model. Declare all common properties of the three data types without an initial value, individual properties with a initial value to use the implicit memberwise initializer to create the Item instance and assign values to the individual properties depending on the type. The sample properties are from the JSON and only an example

struct Item {
  // common properties
  var type : DataType
  var source : String
  var created : String

  // individual properties
  var username = ""
  var description = ""
  var picture = ""
}

In the TableView controller create the data source array

var modelTimeline = [Item]()

and create Item instances while parsing the JSON

...
if let demos = response.result.value as? [JSONDictionary] {
  for demo in demos {
    if let type = demo["type"] as? String {
      let source = type["source"] ?? ""
      let created = type["date"] ?? ""
      var item = Item(type: DataType(rawValue:type), source: source, created: created)
      if type == "demo" {
        if let demo = demo["demo"] as? JSONDictionary, profile = demo["profile"] as? JSONDictionary {
          item.username = profile["username"] ?? ""
          item.description = profile["description"] ?? ""
          item.picture = profile["picture"] ?? ""
        }
      }
      modelTimeline.append(item)
    }
  }
} else { print("not working") }
...

In cellForRowAtIndexPath create cells and assign values to the UI elements depending on the type

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
  let item = modelTimeline[indexPath.row]
  switch item.type {
    case .Demo:
    let cell = tableView.dequeueReusableCellWithIdentifier("DemoCell", forIndexPath: indexPath) as! DemoUItableViewCell
    cell.usernameLabel.text = item.username
    cell.descriptionLabel.text = item.description
    cell.sourceLabel.text = item.source
    // populate other UI elements
    return cell

    case .Comment:
    cell = tableView.dequeueReusableCellWithIdentifier("CommentCell", forIndexPath: indexPath)  as! CommentUItableViewCell
    cell.sourceLabel.text = item.source
    // populate other UI elements
    return cell

    case .Entry:
    cell = tableView.dequeueReusableCellWithIdentifier("EntryCell", forIndexPath: indexPath)  as! EntryUItableViewCell
    cell.sourceLabel.text = item.source
    // populate other UI elements
    return cell
  }
}

The code is not a full working version, it's just a suggestion how to use different cells for different types.