Article List View Controller Swift Program
//
// ArticleListViewController.swift
// TUM Blog
//
import UIKit
import Alamofire
import SwiftyJSON
/**
Controller object that manages the presentation of and interaction with TUM Blog Articles in form of a list.
*/
class ArticleListViewController: UITableViewController {
/// The Articles retrieved from the server.
var articles: [Article] = []
/// Instance to parse ISO date strings into `NSDate` objects.
let dateParser: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
return formatter
}()
/// Instance to format `NSDate`s for display.
let dateFormatter: NSDateFormatter = {
let formatter = NSDateFormatter()
formatter.doesRelativeDateFormatting = true
formatter.dateStyle = NSDateFormatterStyle.ShortStyle
formatter.timeStyle = NSDateFormatterStyle.ShortStyle
formatter.timeZone = NSTimeZone.localTimeZone()
return formatter
}()
// Called after the controller's view is loaded into memory.
override func viewDidLoad() {
super.viewDidLoad()
// Size table view cells via Auto Layout.
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 60
// Load initial data.
loadData()
}
// MARK: - Exercise 3.1 & Exercise 3.2
/// Called when data needs to be loaded from the server.
func loadData() {
// Exercise 3.1 & Exercise 3.2
request(.GET, "\(Shared.Server)/articles")
.validate()
.backgroundResponseJSON { request,response,rawJSONResult in
print("Finally received response")
// Parse raw JSON if a valid result was received.
let parsedArticles = rawJSONResult.value
.flatMap({ self.parseJSON($0) })
dispatch_async(dispatch_get_main_queue()) {
// If retrieving and parsing of the Articles was successful, update UI.
if let articles = parsedArticles {
self.articles = articles
self.tableView.reloadData()
}
// Notify UI that the pull-to-refresh was processed.
self.refreshControl?.endRefreshing()
}
}
print("Scheduled request from server")
}
// MARK: - Final Exercise
/** Called when an Article needs to be saved to the server.
- parameter article: the Article to be saved
* article.id is nil for new Comments
* article.id is set to a specific id for existing Comments
*/
func saveArticle(article: Article) {
let params:[String:AnyObject]
params = [
"email" : article.email,
"title": article.title,
"text": article.text
]
if let id = article.id {
request(.PUT, "\(Shared.Server)/articles/\(id)", parameters: params)
.validate()
.response { request, response, data, error in
self.loadData()
}
} else {
request(.POST, "\(Shared.Server)/articles/", parameters: params)
.validate()
.response { request, response, data, error in
self.loadData()
}
}
}
/// Called when an Article needs to be deleted from the server.
func deleteArticle(id: Int, indexPath: NSIndexPath) {
articles.removeAtIndex(indexPath.row)
request(.DELETE, "\(Shared.Server)/articles/\(id)")
.validate()
.response { request, response, data, error in
self.loadData()
}
}
// MARK: - Parse Response
/// Parses the raw json into an Article object.
func parseJSON(rawJSON: AnyObject) -> [Article]? {
// Return `nil` if the JSON is not an array.
guard let jsonArray = JSON(rawJSON).array else { return nil }
// Iterate through the JSON array, parse each element, and store results in an array.
var parsedArticles = [Article]()
for json in jsonArray {
// Skip element if it does not match expected schema.
guard let id = json["id"].int,
let title = json["title"].string,
let email = json["email"].string,
let text = json["text"].string,
let updatedString = json["updated"].string
else { continue }
parsedArticles.append(Article(
id: id,
title: title,
updated: dateParser.dateFromString(updatedString),
email: email,
text: text))
}
return parsedArticles
}
// MARK: - Table view data source
// Tells the data source to return the number of rows in a given section of a table view.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return articles.count
}
// Asks the data source for a cell to insert in a particular location of the table view.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let article = articles[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier("blogEntry", forIndexPath: indexPath) as! ArticleListViewCell
cell.headingLabel.text = article.title
cell.dateLabel.text = article.updated.flatMap({ dateFormatter.stringFromDate($0) })
if article.email == Shared.YourEmail {
cell.accessoryType = .DetailDisclosureButton
}
if let image = article.profileImage {
cell.profileView.image = image
} else {
Gravatar.getImageForEmail(article.email, forImageView: cell.profileView) { image in
if let image = image {
dispatch_async(dispatch_get_main_queue()) {
article.profileImage = image
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
}
}
}
return cell
}
// Asks the data source to verify that the given row is editable.
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return articles[indexPath.row].email == Shared.YourEmail
}
// Asks the data source to commit the insertion or deletion of a specified row in the receiver.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
guard let id = articles[indexPath.row].id
where editingStyle == .Delete
else {
self.loadData()
return
}
deleteArticle(id, indexPath: indexPath)
}
// MARK: - UI Actions
/// Called when the user "pulls to refresh".
@IBAction func refreshTriggered(sender: AnyObject) {
loadData()
}
// Called when the user wants to cancel the edit and the storyboard is unwound to the Article list.
@IBAction func cancelEditArticle(sender: UIStoryboardSegue) {
print("Canceled Edit")
}
// Called when the user wants to save the edit and the storyboard is unwound to the Article list.
@IBAction func saveEditArticle(sender: UIStoryboardSegue) {
print("Saving Edit")
let newArticleViewController = sender.sourceViewController as! ArticleEditViewController
let article = newArticleViewController.articleDetailsViewController.article
saveArticle(article)
}
// Notifies the view controller that a segue is about to be performed.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
super.prepareForSegue(segue, sender: sender)
guard let cell = sender as? ArticleListViewCell,
let indexPath = tableView.indexPathForCell(cell) else { return }
if let detailsViewController = segue.destinationViewController as? ArticleDetailsViewController {
detailsViewController.article = articles[indexPath.row]
} else if segue.identifier == "editBlogEntry",
let navController = segue.destinationViewController as? UINavigationController,
let editViewController = navController.viewControllers.first as? ArticleEditViewController {
editViewController.article = articles[indexPath.row]
}
}
}
If the answers is incorrect or not given, you can answer the above question in the comment box. If the answers is incorrect or not given, you can answer the above question in the comment box.