@mizumotokのブログ

テクノロジー、投資、書評、映画、筋トレなどについて

Swift4でRSSリーダーをつくろう

以前の、Swiftで作成したRSSリーダーの記事が好評でしたがSwiftのバージョンもあがって古くなったので作り直しました。

RSSリーダーはTableViewを使いますし、データの通信もあるので、サンプルとしては手頃です。
今回はシンプルにYahoo!トピックスの見出しのみを表示して、内容はSafariに飛ばして表示するようにしています。

f:id:mizumotok:20180412161545j:plain

ストーリーボード

まずはXcodeを立ちあげて、Single View Applicationから作成します。
言語はもちろんSwiftです。

Main.storyboardのViewの上にTableViewを配置します。
TableViewを右クリックしてdataSourceとdelegateをViewControllerにつなげておきます。
また ViewController.swift に Outlet で紐付けます。

f:id:mizumotok:20180412161844j:plain

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!

ViewController.swiftの実装

ViewControllerにはUITableViewDelegate、UITableViewDataSourceに加えてXMLParserDelegateプロトコルも適合します。XMLParserはSwift標準ライブラリのXMLパーサーです。

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, XMLParserDelegate {

RSSのURLを指定します。今回はソースに直書きです。

    let feedUrl = URL(string: "https://news.yahoo.co.jp/pickup/rss.xml")!

RSSのitemを格納するためにFeedItemクラスを用意します。
またそれらを配列に保持しておきます。

    var feedItems = [FeedItem]()
class FeedItem {
    var title: String!
    var url: String!
}

あとは表示用のUITableViewDelegateプロトコルRSSパース用のXMLParserDelegateプロトコロのメソッドを適切に実装していくだけです。


ソースコード

ViewController.swift

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, XMLParserDelegate {

    @IBOutlet weak var tableView: UITableView!

    let feedUrl = URL(string: "https://news.yahoo.co.jp/pickup/rss.xml")!
    var feedItems = [FeedItem]()

    var currentElementName : String! // RSSパース中の現在の要素名
    
    let ITEM_ELEMENT_NAME = "item"
    let TITLE_ELEMENT_NAME = "title"
    let LINK_ELEMENT_NAME   = "link"

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.feedItems.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "Cell")
        let feedItem = self.feedItems[indexPath.row]
        cell.textLabel?.text = feedItem.title
        return cell
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let feedItem = self.feedItems[indexPath.row]
        UIApplication.shared.open(URL(string: feedItem.url)!, options: [:], completionHandler: nil)
    }
    
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
        self.currentElementName = nil
        if elementName == ITEM_ELEMENT_NAME {
            self.feedItems.append(FeedItem())
        } else {
            currentElementName = elementName
        }
    }
    
    func parser(_ parser: XMLParser, foundCharacters string: String) {
        if self.feedItems.count > 0 {
            let lastItem = self.feedItems[self.feedItems.count - 1]
            switch self.currentElementName {
            case TITLE_ELEMENT_NAME:
                let tmpString = lastItem.title
                lastItem.title = (tmpString != nil) ? tmpString! + string : string
            case LINK_ELEMENT_NAME:
                lastItem.url = string
            default: break
            }
        }
    }
    
    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        self.currentElementName = nil
    }
    
    func parserDidEndDocument(_ parser: XMLParser) {
        self.tableView.reloadData()
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let parser: XMLParser! = XMLParser(contentsOf: feedUrl)
        parser.delegate = self
        parser.parse()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

class FeedItem {
    var title: String!
    var url: String!
}