【Swift】Yahooのテキスト解析APIを使用して五十音順ソートを実装する

おはこんばんにちは、しょーです。

以前とある入社試験で、漢字にルビを取得して表示する簡単なアプリの作成をしたのですが、そのAPIをKaraokeyに使えるなぁと思ったので実行することに。
今回やることは

テキスト解析APIを叩いて、漢字を含んだリストをあいうえを順に並べる

です。

さてお茶でも飲みながら行ってみよう(ZONE飲んでる)。

使用するAPI

今回使用するAPIはYahooディベロッパーネットワークから出ているルビ振りAPIです。

https://developer.yahoo.co.jp/webapi/jlp/furigana/v1/furigana.html

目的

テキスト解析APIを叩いて、漢字を含んだリストをあいうえを順に並べる。
したはもう完成図ですが、A-Zあいうえお順に並び替えます。

公式サイトで使用するアプリケーションの登録をする

こちらに必要事項を入力していきます。

そうするとアプリケーションIDが発行されるので、パラメーターに変換したいテキストと一緒に付与してリクエストを送ってくれればおkです。

リクエストを作成する

リクエストを作成していくぞー。
大体レスポンスはJSONだろー、いつも使ってるテンプレートでっと、、、

しかししょーはまだ気付いていなかった。
レスポンスはJSONではなく、XMLだということを、、!

課題に取り組んでいるときはSwiftyJSONでJSON形式しか触ったことなかったのでどうしよう〜でした。

そんな過去の話は置いておき、以下の便利なライブラリを使用してみます。

SWXMLHash

SwiftyJSON風にかけるXML用のライブラリです。
こちらをAlamofireと組み合わせて使っていきます。

private func fetchRuby(text: String, completion: @escaping ((String) -> Void)) -> Thunk<AppState> {
    return Thunk<AppState> { dispatch, getState in
    let params = [
        "appid": "XXXXXXXXXXXXXXXXXXX",
        "sentence": text,
    ]
    AF.request("https://jlp.yahooapis.jp/FuriganaService/V1/furigana",
               method: .get,
               parameters: params,
               encoding: URLEncoding.default,
               headers: nil
        )
        .response { (response) in
            guard let data = response.data else {
                print("cannot get XML")
                return
            }
            let xml = SWXMLHash.parse(data)
            var ruby = ""
            let wordCount = xml["ResultSet"]["Result"]["WordList"]["Word"].all.count

            if wordCount == 0 {
                print("word count is 0")
            }else{
                for i in 0...wordCount - 1 {
                    if let furigana = xml["ResultSet"]["Result"]["WordList"]["Word"][i]["Furigana"].element?.text {
                        ruby += furigana
                    }else if let notFurigana = xml["ResultSet"]["Result"]["WordList"]["Word"][i]["Surface"].element?.text {
                        ruby += notFurigana
                    }
                }
                completion(ruby)
            }
        }
    }
}

リクエストはこんな感じ。センスがなくてすみません。一年前に書いたやつをコピペして使ってます。
ちなみにXMLレスポンスはこのように返ってきます。

<ResultSet xsi:schemaLocation="urn:yahoo:jp:jlp:FuriganaService https://jlp.yahooapis.jp/FuriganaService/V1/furigana.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:jp:jlp:FuriganaService">
  <Result>
    <WordList>
      <Word>
        <Surface>白日</Surface>
        <Furigana>はくじつ</Furigana>
        <Roman>hakuzitu</Roman>
      </Word>
    </WordList>
  </Result>
</ResultSet>

これを に到達するまでポチポチ下がっていけばおkです。

let furigana = xml["ResultSet"]["Result"]["WordList"]["Word"][i]["Furigana"].element?.text

A-Z五十音順にソートする

上記のリクエストメソッドでは完了ハンドラをつけているので、レスポンスの振り仮名が返るようになっています。
それを利用して、次のソートメソッド x Entity追加メソッドに渡してあげる。

public func createNewSong(title: String, artist: String, key: Int, tags: [String], completion: @escaping (([Entity.Song]) -> Void)) -> Thunk<AppState> {
    return Thunk<AppState> { dispatch, getState in
        dispatch(fetchRuby(text: title, completion: { kana in
            let entity = Entity.Song(id: 999, title: title, kana: kana, artist: artist,
                                    rate: 0, key: key, tags: tags)
            var songs = store.state.songsState.allSongsState.allSongs // stateで管理してる全曲分リストデータ
            // appending antity
            songs.append(entity)
            // sort by alphabet
            let alphabeticalList = songs.sorted(by: { $0.title < $1.title }) // ここでソート
            // modify id
            let changedIdList = alphabeticalList.enumerated().map { (index: Int, song: Entity.Song) -> Entity.Song in
                var track = song
                track.id = index
                return track
            }
            dispatch(SongsState.updateSongs(changedIdList))

            completion(changedIdList)
        }))

    }
}

Swiftにはデフォルトでソートメソッドが用意してあります。
構造体のプロパティを利用してソートしたい場合、sort() が利用できるので使ってみましょう。

今回は、

  • アルファベットはA-Z
  • 日本語は五十音順

でソートしたいので

let alphabeticalList = songs.sorted(by: { $0.title < $1.title })

としました。
これを $0.title > $1.title とすると、結果が逆になります。

あとはEntityにルビをプロパティとして与えてあげて、それを元にTableViewに表示すればOKです。

まとめ

大まかな流れとしては

  • Yahoo API で取得したひらがなを取得
  • 完了ハンドラでソート&Entity作成メソッドにルビを渡してあげる
  • ルビをプロパティに持ったEntityを作成し、全曲リストに追加
  • 全曲リストをソート
  • tableViewで表示

こんな感じです。

このリストにTableViewHeaderなどを作成すれば、いい感じの五十音順リストが作れるかと思いますよ!

もしよかったら楽チンなAPIなので使ってみてくれよな!

お疲れさん!
じゃーな!