title: Swift 3 Sort / Group JSON Data into Tableview with Sections
body: <p>I need to sort / group JSON Data into sections on a tableview. </p>
  
  <p>Right now, the JSON data can't be easily altered so it needs to be re-sorted in Swift. </p>
  
  <p>Basically, I would like it to count how many different DATES there are (which is just a string value) and sort the data based upon it. Then, the sections should be the different DATES with the respected articles in the rows. </p>
  
  <p><strong>The tableview should look like this:</strong></p>
  
  <pre><code>**January 1, 2016**
  - Article A 
       - Title
       - Author
       - ETC...
  - Article C
       - Title
       - Author
       - ETC...
  **February 2, 2016**
  - Article B
       - Title
       - Author
       - ETC...
  </code></pre>
  
  <p><strong>Right now the JSON looks something like:</strong></p>
  
  <pre><code>[
      {
          “x_author”: "",
          "excerpt" : 
              "rendered": “”
          },
          "x_featured_media" : "",
          "title": {
              "rendered": “A”
          },
          "x_date": “January 1, 2016",
      },
      {
          “x_author”: "",
          "excerpt" : 
              "rendered": “”
          },
          "x_featured_media" : "",
          "title": {
              "rendered": “B”
          },
          "x_date": “February 1, 2016",
      },
      {
          “x_author”: "",
          "excerpt" : 
              "rendered": “”
          },
          "x_featured_media" : "",
          "title": {
              "rendered": “C”
          },
          "x_date": “January 1, 2016",
      },
  ]
  </code></pre>
  
  <p><strong>The ViewController looks like this:</strong></p>
  
  <pre><code>import UIKit
  
  class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
      @IBOutlet var tableview: UITableView!
  
      var articles: [Article]? = []
  
          override func viewDidLoad() {
          super.viewDidLoad()
          // Do any additional setup after loading the view, typically from a nib.
          fetchArticles()
      }
  
      func fetchArticles() {
          let urlRequest = URLRequest(url: URL(string: "SITE")!)
          let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
              if error != nil {
                  print(error)
                  return
              }
              self.articles = [Article]()
              do {
                  let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? AnyObject
                  if let articlesFromJson = json as? [[String : AnyObject ]] {
                      for articleFromJson in articlesFromJson {
                          let article = Article()
                            if let title = articleFromJson["title"]?["rendered"] as? String, 
                                let author = articleFromJson["x_author"] as? String, 
                                let desc = articleFromJson["excerpt"]?["rendered"] as? String, 
                                let url = articleFromJson["link"] as? String, 
                                let urlToImage = articleFromJson["x_featured_media"] as? String, 
                                let date = articleFromJson["x_date"] as? String {
  
                              article.author = author
                              article.desc = desc
                              article.headline = title
                              article.url = url
                              article.imageUrl = urlToImage
                              article.date = date
                          }
                          self.articles?.append(article)
                       }
                  }
                  DispatchQueue.main.async {
                      self.tableview.reloadData()
                  }
              } catch let error {
                  print(error)
              }
          }
          task.resume()
      }
  
      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
          let cell = tableView.dequeueReusableCell(withIdentifier: "articleCell", for: indexPath) as! articleCell
          cell.title.text = self.articles?[indexPath.item].headline
          cell.desc.text = self.articles?[indexPath.item].desc
          cell.author.text = self.articles?[indexPath.item].author
          cell.date.text = self.articles?[indexPath.item].date    
          cell.imgView.downloadImage(from: (self.articles?[indexPath.item].imageUrl!)!)
          return cell
      }
  
      func numberOfSections(in tableView: UITableView) -&gt; Int {
          return 1
      }
  
      func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
          return self.articles?.count ?? 0
      }
  
      func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
          let webVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "web") as! WebviewViewController
          webVC.url = self.articles?[indexPath.item].url
          self.present(webVC, animated: true, completion: nil)
      }
  }
  </code></pre>
  
  <p><strong>The Article class looks like:</strong></p>
  
  <pre><code>import UIKit
  
  class Article: NSObject {
      var headline: String?
      var desc: String?
      var author: String?
      var url: String?
      var content: String?
      var imageUrl: String?
      var date: String?
  }
  </code></pre>
  
  <p><strong>How do I go about doing this?</strong></p>
  
  <p><strong>EDIT 1:</strong></p>
  
  <p>Following the advice of dmorrow, the view controller now looks like this:</p>
  
  <pre><code>    struct Issue {
      let dateName: String?
      var articles: [Article]?
  }
  struct Article {
      var headline: String?
      var desc: String?
      var author: String?
      var url: String?
      var imageUrl: String?
      var date: String?
  }
  
  var groupedArticles = [Issue]()
  
      override func viewDidLoad() {
      super.viewDidLoad()
      // Do any additional setup after loading the view, typically from a nib.
      fetchArticles()
  }
  
  func fetchArticles() {
      let urlRequest = URLRequest(url: URL(string: "SITE")!)
      let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
          if error != nil {
              print(error)
              return
          }
          do {
              let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? AnyObject
              if let articlesFromJson = json as? [[String : AnyObject ]] {
                  for articleFromJson in articlesFromJson {
  
                          self.groupedArticles.append(Issue(dateName: (articleFromJson["x_date"] as! String), articles: [Article(headline: (articleFromJson["title"] as! String), desc: (articleFromJson["excerpt"] as! String), author: (articleFromJson["x_author"] as! String), url: (articleFromJson["x_featured_media"] as! String), imageUrl: (articleFromJson["x_featured_media"] as! String), date: (articleFromJson["x_date"] as! String))]))                    
                     }
              }
              DispatchQueue.main.async {
                  self.tableview.reloadData()
              }
          } catch let error {
              print(error)
          }
      }
      task.resume()
  }
  
  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "articleCell", for: indexPath) as! articleCell        
      cell.title.text = self.groupedArticles[indexPath.section].articles?[indexPath.row].headline
      cell.desc.text = self.groupedArticles[indexPath.section].articles?[indexPath.row].desc
      cell.author.text = self.groupedArticles[indexPath.section].articles?[indexPath.row].author
      cell.date.text = self.groupedArticles[indexPath.section].articles?[indexPath.row].date
  
      cell.imgView.downloadImage(from: (self.groupedArticles[indexPath.section].articles?[indexPath.row].imageUrl!)!)
      return cell
  }
  
  func numberOfSections(in tableView: UITableView) -&gt; Int {
      return groupedArticles.count
  }
  
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -&gt; Int {
      return groupedArticles[section].articles?.count ?? 0
  }
  
  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
      let webVC = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "web") as! WebviewViewController
      webVC.url = self.groupedArticles[indexPath.section].articles?[indexPath.row].url
      self.present(webVC, animated: true, completion: nil)
  }
  </code></pre>
  
  <p><strong>HOWEVER, getting error at:</strong></p>
  
  <blockquote>
    <p>self.groupedArticles.append(Issue(dateName: (articleFromJson["x_date"]
    as! String), articles: [Article(headline: (articleFromJson["title"]
    as! String), desc: (articleFromJson["excerpt"] as! String), author:
    (articleFromJson["x_author"] as! String), url:
    (articleFromJson["x_featured_media"] as! String), imageUrl:
    (articleFromJson["x_featured_media"] as! String), date:
    (articleFromJson["x_date"] as! String))]))</p>
  </blockquote>
  
  <p>"Could not cast value of type '__NSDictionaryM' (0x106a35260) to 'NSString' (0x1031d9c40)."</p>
  
  <p><strong>Was this the right track? How do I solve this?</strong></p>
  
  <hr>
  
  <h2>EDIT 2</h2>
  
  <p>Updated fetchArticles function:</p>
  
  <pre><code>func fetchArticles() {
      let urlRequest = URLRequest(url: URL(string: "X")!)
      let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
          if error != nil {
              print(error)
              return
          }
          do {
              let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? AnyObject
              if let articlesFromJson = json as? [[String : AnyObject]] {
                  for articleFromJson in articlesFromJson {
                      // Get All Articles
                      var articleEntries = [Issue]()
  
                      articleEntries.append(Issue(dateName: (articleFromJson["x_date"] as! String), articles: [Article(headline: (articleFromJson["title"]?["rendered"] as! String), desc: (articleFromJson["excerpt"]?["rendered"] as! String), author: (articleFromJson["x_author"] as! String), url: (articleFromJson["x_featured_media"] as! String), imageUrl: (articleFromJson["x_featured_media"] as! String), date: (articleFromJson["x_date"] as! String))]))
  
                      // SORT Articles by KEY of X_Date(String)
                      var groupedArts = [String: [Issue]]()
                      var groupedKeys = Array&lt;String&gt;()
  
                      for article in articleEntries {
                          let index = (article.dateName?.startIndex)!..&lt;(article.dateName?.endIndex)!
                          let keys = String(describing: article.dateName?[index])
                          if groupedArts[keys] != nil {
                              groupedArts[keys]?.append(article)
                              print("this array exists")
                          } else {
                              groupedArts[keys] = [article]
                              groupedKeys.append(keys)
                              print("this array does not exist")
                          }
                      }
                      print(groupedArts)
                      print(groupedKeys)
                  }
              }
              DispatchQueue.main.async {
                  self.tableview.reloadData()
              }
          } catch let error {
              print(error)
          }
      }
      task.resume()
  }
  </code></pre>
  
  <p>From my understanding, I should be parsing the entries then re-ordering them using something similar to the sample code but its producing results individually:</p>
  
  <blockquote>
    <p>["Optional(\"February 14, 2016\")":
    [Journal.ViewController.Issue(dateName: Optional("February 14, 2016"),
    articles: Optional([Journal.ViewController.Article(headline:
    Optional("Case File: Shocking out with severe hypoxia"), desc:
    Optional("ABC"), author:
    Optional("AUTHOR"), url: Optional("X"), imageUrl: Optional("X"), date:
    Optional("February 14, 2016"))]))]] ["Optional(\"February 14,
    2016\")"]</p>
  </blockquote>
  
  <p>and so forth for each article. I tried to parse the articles in the IF statement but that crashes everything. What can I do?</p>