| // DetailViewController.swift |
| // |
| // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/) |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a copy |
| // of this software and associated documentation files (the "Software"), to deal |
| // in the Software without restriction, including without limitation the rights |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| // copies of the Software, and to permit persons to whom the Software is |
| // furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| // THE SOFTWARE. |
| |
| import Alamofire |
| import UIKit |
| |
| class DetailViewController: UITableViewController { |
| enum Sections: Int { |
| case Headers, Body |
| } |
| |
| var request: Alamofire.Request? { |
| didSet { |
| oldValue?.cancel() |
| |
| title = request?.description |
| refreshControl?.endRefreshing() |
| headers.removeAll() |
| body = nil |
| elapsedTime = nil |
| } |
| } |
| |
| var headers: [String: String] = [:] |
| var body: String? |
| var elapsedTime: NSTimeInterval? |
| var segueIdentifier: String? |
| |
| static let numberFormatter: NSNumberFormatter = { |
| let formatter = NSNumberFormatter() |
| formatter.numberStyle = .DecimalStyle |
| return formatter |
| }() |
| |
| // MARK: View Lifecycle |
| |
| override func awakeFromNib() { |
| super.awakeFromNib() |
| refreshControl?.addTarget(self, action: #selector(DetailViewController.refresh), forControlEvents: .ValueChanged) |
| } |
| |
| override func viewDidAppear(animated: Bool) { |
| super.viewDidAppear(animated) |
| refresh() |
| } |
| |
| // MARK: IBActions |
| |
| @IBAction func refresh() { |
| guard let request = request else { |
| return |
| } |
| |
| refreshControl?.beginRefreshing() |
| |
| let start = CACurrentMediaTime() |
| request.responseString { response in |
| let end = CACurrentMediaTime() |
| self.elapsedTime = end - start |
| |
| if let response = response.response { |
| for (field, value) in response.allHeaderFields { |
| self.headers["\(field)"] = "\(value)" |
| } |
| } |
| |
| if let segueIdentifier = self.segueIdentifier { |
| switch segueIdentifier { |
| case "GET", "POST", "PUT", "DELETE": |
| self.body = response.result.value |
| case "DOWNLOAD": |
| self.body = self.downloadedBodyString() |
| default: |
| break |
| } |
| } |
| |
| self.tableView.reloadData() |
| self.refreshControl?.endRefreshing() |
| } |
| } |
| |
| private func downloadedBodyString() -> String { |
| let fileManager = NSFileManager.defaultManager() |
| let cachesDirectory = fileManager.URLsForDirectory(.CachesDirectory, inDomains: .UserDomainMask)[0] |
| |
| do { |
| let contents = try fileManager.contentsOfDirectoryAtURL( |
| cachesDirectory, |
| includingPropertiesForKeys: nil, |
| options: .SkipsHiddenFiles |
| ) |
| |
| if let |
| fileURL = contents.first, |
| data = NSData(contentsOfURL: fileURL) |
| { |
| let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) |
| let prettyData = try NSJSONSerialization.dataWithJSONObject(json, options: .PrettyPrinted) |
| |
| if let prettyString = NSString(data: prettyData, encoding: NSUTF8StringEncoding) as? String { |
| try fileManager.removeItemAtURL(fileURL) |
| return prettyString |
| } |
| } |
| } catch { |
| // No-op |
| } |
| |
| return "" |
| } |
| } |
| |
| // MARK: - UITableViewDataSource |
| |
| extension DetailViewController { |
| override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { |
| switch Sections(rawValue: section)! { |
| case .Headers: |
| return headers.count |
| case .Body: |
| return body == nil ? 0 : 1 |
| } |
| } |
| |
| override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { |
| switch Sections(rawValue: indexPath.section)! { |
| case .Headers: |
| let cell = tableView.dequeueReusableCellWithIdentifier("Header")! |
| let field = headers.keys.sort(<)[indexPath.row] |
| let value = headers[field] |
| |
| cell.textLabel?.text = field |
| cell.detailTextLabel?.text = value |
| |
| return cell |
| case .Body: |
| let cell = tableView.dequeueReusableCellWithIdentifier("Body")! |
| cell.textLabel?.text = body |
| |
| return cell |
| } |
| } |
| } |
| |
| // MARK: - UITableViewDelegate |
| |
| extension DetailViewController { |
| override func numberOfSectionsInTableView(tableView: UITableView) -> Int { |
| return 2 |
| } |
| |
| override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { |
| if self.tableView(tableView, numberOfRowsInSection: section) == 0 { |
| return "" |
| } |
| |
| switch Sections(rawValue: section)! { |
| case .Headers: |
| return "Headers" |
| case .Body: |
| return "Body" |
| } |
| } |
| |
| override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { |
| switch Sections(rawValue: indexPath.section)! { |
| case .Body: |
| return 300 |
| default: |
| return tableView.rowHeight |
| } |
| } |
| |
| override func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? { |
| if Sections(rawValue: section) == .Body, let elapsedTime = elapsedTime { |
| let elapsedTimeText = DetailViewController.numberFormatter.stringFromNumber(elapsedTime) ?? "???" |
| return "Elapsed Time: \(elapsedTimeText) sec" |
| } |
| |
| return "" |
| } |
| } |