post Image
日本語 Codable Enums

この記事は iOS Advent Calendar 2018 の 6日目の記事です。

Codable1 は、Swift 4.0 (3.2) で登場し、今年リリースされた Swift 4.1 / 4.2 のアップデートによって、さらに使いやすくなりました。

この記事では、実際に今年iOSアプリ開発で遭遇したケースを元に2、 enum、 Codable など、 Swift らしい機能を使って JSON を簡潔に扱う例を紹介します。

想定ケース

「やるべき宿題(タスク)を表示してくれるリスト」を表示する画面を作ります。

API からは以下のような、タスクの配列が返ってきます。

{
    [
    id: 1,  // タスクID
    subject_type: 2, // 教科タイプ
    books: [ ... ] ... // やるべき本とかとか
    ], ...
}

宿題には、教科のタイプ(国語、英語とか)が含まれます。

これを、リストの各要素に表示する画面を作ります。教科ごとに色が決まっていて、リストの要素の背景や、文字などに使用します。

また、教科のリストを選んで設定画面も作ります。

まとめると以下のようになります。

  • 課題の配列を、リストに表示する画面がある
  • 教科タイプ (subject_type) は、整数型で返ってくる
  • 教科の情報 (教科名, 色など) があり、クライアントで持っている
  • 教科タイプ (subject_type) をユーザーに選んでもらって、 post してもらう画面がある

JSON のパースには Codable を使うとよさそうですね。使ってみましょう。

実装例

以下のような型を定義しました。

struct Task: Codable {
    let id: Int
    let books: [Book]
    let subjectType: SubjectType
}

struct Book: Codable {
    let id: Int
    let title: Int
    ...
}

enum SubjectType: Int, Codable, CaseIterable, CaseStringConvertible {
    case 数学
    case 英語
    case 倫理
    case 簿記会計
    case 生物基礎

    var color: UIColor {
        switch self {
        case .数学: return .blue
        case .英語: return .red
        case .倫理: return .yellow
        case .簿記会計: return .brown
        case .生物基礎: return .green
        }
    }
}

注目してほしいのは SubjectType です。 Codable で、CaseIterable で、 CaseStringConvertible な Int の enum になっています。らぶるらぶる。

subjectType は単なる Int で指定しても実装できてしまうのですが、enum な型にするのには以下のようなメリットがあります。

  • 他の型でも subjecType が登場するとき、それらが同一のものであることを示せる (型にするメリット)
  • switch 文で使うとき、 default: を使わなければ case が増えたときにビルドエラーになり、もれなく列挙できる (enum を使うメリット)

Int の enum は、何も指定しない場合は 0 から値が割り当てられ、 subject_type の値が 0 なら数学、 1 なら英語に map されます。特定の値を特定の case としたいときには数値を指定することもできます。

    case 数学 = 5
    case 英語

ちなみに、JSONのほうのキーが snake_case になっている場合は、JsonDecoderkeyDecodingStrategy を指定することで camelCase に変換してくれます(Swift 4.1〜)。

let jsonDecoder = JsonDecoder()
jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase

これ以外にも、テクニックというと大げさですが、簡潔に扱うために工夫している部分があるので、ここでは3つほど紹介します。

テクニック1: 日本語をつかう

これは早速好みの分かれそうな方法ですが、case の名前に日本語を使っています。

日本語を使うことにはもちろんデメリットもあります。そのため、辞書を使っていい感じの英語に英訳できる場合は、英語を使うことも多いのですが、今回のような例だと…

    case 数学
    case 英語
    case 倫理
    case 簿記会計
    case 生物基礎

律儀に英訳していくのは結構たいへんではないでしょうか?(特に「簿記・会計」とか…)

この場合は、 case の名前に、無理せず日本語をそのまま使うことで、可読性や保守性を上げることができるケースになるのではないかと思います(そして、後述する副次的な効果もあります)。

都道府県名なども日本固有の名前なので、日本語にしてしまうのがおすすめの例です。

enum Prefecture {
    case 北海道
    case 青森県
    case 岩手県
    ...
}

日本語命名は便利ですが使う場所によってはリスクもあり、たとえば Swift4.2 では、 Storyboard に紐付いている ViewController の名前などに指定していると、インスタンス化に失敗することがあります。しかし enum の case 名であれば、まず問題ないと思います。

テクニック2: String(describing:) をつかう

SubjectTypeCaseStringConvertible というユーザー定義のプロトコルに適合していました。これは、 String(describing: self) 3を返すプロトコルです。

protocol CaseStringConvertible { }

extension CaseStringConvertible {
    var string: String {
        return String(describing: self)
    }
}

hoge.string // それ自身を表現する文字列

String(describing: self) は、それ自身を表現する文字列を返してくれます。たとえば、 Int の enum のインスタンスを渡したときは、case の名前になります。print 関数にインスタンスを与えたときに出力される内容は、これと同じです。

case名を日本語にしたいのはこれを使いたいからでもあります。UIの表示名に合わせておくことで、 UIに表示すべき名前を インスタンス名.string で簡潔に取り出すことができます。

subjectLabel.text = task.type.string // 生物基礎 とかが返ってくる
task.type.rawValue // Int そのものの値が欲しいとき

また、色を返す変数を用意していたので、同じように .color でシンプルに取り出すことができます。

    var color: UIColor {
        switch self {
        case .数学: return .blue
        case .英語: return .red
        case .倫理: return .yellow
        case .簿記会計: return .brown
        case .生物基礎: return .green
        }
    }
}
...

cell.subjectView.backgroundColor = task.type.color

これでリストの要素を簡潔に表現することができそうです。

テクニック3: CaseIterable をつかう

Swift 4.2 で、CaseIterable というプロトコルが増えました4。これに適合させた enum は、 .allCases で case のコレクションが取れるようになりました。

public protocol CaseIterable {
    /// A type that can represent a collection of all values of this type.
    associatedtype AllCases : Collection where Self.AllCases.Element == Self

    /// A collection of all values of this type.
    public static var allCases: Self.AllCases { get }
}

たとえばユーザーに SubjectType を選択してもらうリストを作りたい場合、 .allCases を使って表示するべき内容を指定することができます。

func collectionView(_ collectionView: UICollectionView,
                    cellForItemAt indexPath: IndexPath)
    -> UICollectionViewCell {
    let cell = collectionView
        .dequeueReusableCell(with: MyCell.self, for: indexPath)
    cell.setUp(title: SubjectType.allCases[indexPath.row].string)
}

func collectionView(_ collectionView: UICollectionView,
                    didSelectItemAt indexPath: IndexPath) {
    self.selectedType = SubjectType(rawValue: indexPath.row)!
    collectionView.reloadData()
}

便利ですね。

SubjectType は Codable (= JSON から変換できるし、JSON に変換可能)なので、選ばれた教科をそのまま post することができます。これで教科を選択する画面も作れそうです。

なお、 CaseIterable は Swift4.2 で追加されたプロトコルですが、Swift 4.2 以前でも、以下のように自分で定義して使うことができます5

public protocol EnumEnumerable { associatedtype Case = Self }
public extension EnumEnumerable where Case: Hashable {
    private static var iterator: AnyIterator<Case> {
        var n = 0
        return AnyIterator {
            defer { n += 1 }
            let next = withUnsafePointer(to: &n) {
                UnsafeRawPointer($0).assumingMemoryBound(to: Case.self).pointee
            }
            return next.hashValue == n ? next : nil
        }
    }
    public static var allCases: [Case] {
        return Array(self.iterator)
    }
}

まとめ

Codable に適合した Enum を使うことで、 JSON を返す API とのやりとりを簡潔に Swift らしく表現できます。

また、以下と組み合わせることによって、さらに簡潔に表現できる場合があります。

  • case の名前は無理に英訳せず、UI上の表示名を合わせられないか検討する
  • String(describing:) を使う
  • CaseIterable を使う

2019年も enum と Codable を使ってたのしくコーディングしていきましょう! 🎉


『 Swift 』Article List
Category List

Eye Catch Image
Read More

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

AWSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Bitcoinに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

CentOSに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

dockerに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

GitHubに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Goに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Javaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

JavaScriptに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Laravelに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Rubyに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Scalaに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Vue.jsに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

Wordpressに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Eye Catch Image
Read More

機械学習に関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。