※この記事はRSSデータの更新日を取得してTableViewで新しい順に並び替える - Qiitaの記事をエクスポートしたものです。内容が古くなっている可能性があります。
はじめに
エンジニアブログのまとめアプリを作っている時に、ブログの更新日を取得して順番に並び替える機能を実装しました。その際にハマったところをまとめます。
開発環境
完成形
実装
手順は以下です ①各ブログのRSSデータを取得する ②XMLParserで「pubDate」の部分をパースする ③sort(by:)メソッドで並べ替える ④「Sat, 07 Dec 2019 03:17:56 +0000」のような書式で取得されるので「11月7日(土)」のような書式に変換する ⑤TableViewのLabelに表示する
①各ブログのRSSデータを取得する
// ListViewController.swift import UIKit class ListViewController1: UITableViewController, XMLParserDelegate { var parser:XMLParser! var items = [Item]() var item:Item? var currentString = "" let formatter = DateFormatter() let urls = [ URL(string: "https://tech.mercari.com/rss"), //以下略 ] //①各ブログのRSSデータを取得する func startDownload() { self.items = [] for link in urls { if let url = link { if let parser = XMLParser(contentsOf: url) { self.parser = parser self.parser.delegate = self self.parser.parse() } } } }
// Item.swift import Foundation class Item { var title = "" var link = "" var pubDate: Date! }
self.items = [ ]で配列の中身を空にします。次にfor in文でurls[ ]のURLを全て指定します。続いてXMLParserのインスタンスを作成しています。引数にはurls[ ]のURLを指定します。 そしてparserのdelegateに「self」を指定し、最後にparse()メソッドを呼び出すことでデータの解析を開始しています。 引用元:絶対に挫折しないiPhoneアプリ開発「超」入門 第7版 p.332
②XMLParserで「pubDate」の部分をパースする,③sort(by:)メソッドで並べ替える
// ListViewController.swift func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { formatter.locale = Locale(identifier: "en_US_POSIX") formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss ZZZZ" //③sort(by:)メソッドで並べ替える items.sort(by: { (a, b) -> Bool in return a.pubDate > b.pubDate }) switch elementName { case "title": self.item?.title = currentString case "link": self.item?.link = currentString //②XMLParserで「pubDate」の部分をパースする case "pubDate": self.item?.pubDate = formatter.date(from: currentString) case "item": self.items.append(self.item!) default : break } } }
②「pubDate」の開始タグが見つかるとその要素をDate型に変換してitemsに追加します。DateFormatter()が使えなくてハマりました。 ③「pubDate」を「en_US_POSIX」の書式のまま並び替えます。sorted(by:)とごっちゃになってハマりました。
④「Sat, 07 Dec 2019 03:17:56 +0000」のような書式で取得されるので「11月7日(土)」のような書式に変換する
// ListViewController.swift let tempDate = items[indexPath.row].pubDate! formatter.locale = Locale(identifier: "ja_JP") formatter.dateFormat = "MM月dd日(E)" let outputDate = formatter.string(from: tempDate) cell.detailTextLabel?.text = outputDate
String型で取得した日付情報をDate型に変換し、順番を入れ替えてから今度はString型に変換する部分が個人的にハマったポイントです。
⑤TableViewのLabelに表示する
// ListViewController.swift cell.detailTextLabel?.text = outputDate
全体のソースコード
// ListViewController.swift import UIKit class ListViewController1: UITableViewController, XMLParserDelegate { var parser:XMLParser! var items = [Item]() var item:Item? var currentString = "" let formatter = DateFormatter() let urls = [ URL(string: "https://tech.mercari.com/rss"), //以下略 ] override func viewDidLoad() { //テーブルの高さを自動で調節 self.tableView.rowHeight = UITableView.automaticDimension } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) startDownload() } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = items[indexPath.row].title let tempDate = items[indexPath.row].pubDate! formatter.locale = Locale(identifier: "ja_JP") formatter.dateFormat = "MM月dd日(E)" let outputDate = formatter.string(from: tempDate) cell.detailTextLabel?.text = outputDate //ラベルの表示行数を無制限にする cell.textLabel?.numberOfLines = 0 return cell } func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 2 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.count } func startDownload() { self.items = [] for link in urls { if let url = link { if let parser = XMLParser(contentsOf: url) { self.parser = parser self.parser.delegate = self self.parser.parse() } } } } func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { formatter.locale = Locale(identifier: "en_US_POSIX") formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss ZZZZ" items.sort(by: { (a, b) -> Bool in return a.pubDate > b.pubDate }) switch elementName { case "title": self.item?.title = currentString case "link": self.item?.link = currentString case "pubDate": self.item?.pubDate = formatter.date(from: currentString) case "item": self.items.append(self.item!) default : break } } func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) { self.currentString = "" if elementName == "item" { self.item = Item() } } func parser(_ parser: XMLParser, foundCharacters string: String) { self.currentString += string } func parserDidEndDocument(_ parser: XMLParser) { self.tableView.reloadData() }
最後に
他にもっといい方法があるのかもしれませんが、私はこの方法で目的が達成できました。(本題とは関係ありませんが)for in文で配列のRSSデータを取得しているのですが、取得に時間がかかってしまうのでまだまだ改善の余地ありです。