列挙型 enum を使う

本記事ではユーザ定義の Text を包含したビュー MyText のコンストラクタ引数への値の渡し方を使って列挙型 enum について解説しようと思います。

単純なやり方から、リファクタを経て enum を使う方法に近づいていく方法で解説します。結論だけ知りたければ、記事の step3 だけご覧ください。

  • step0: 単純に Int 型の引数として渡す
  • step1: 定義された定数を引数で渡す
  • step2: 定義された enum 定数を引数で渡す
  • step3: 定義された enum 概念を引数として渡す ( 決定版 )

step0: 単純に Int 型の引数として渡す

引数 font_mul に、フォントのサイズ倍率を渡します。font_type にフォントのデザインを渡します。どちらも Int 型の数値で渡しています。プログラマ0年目みたいな初心者的コードです。これは相当まずいです。

9〜12行目で、意味のわからない 1, 2, 3, 4 という数字が出現しています。また、33,38,43,48行目で等価性を即値で評価しているのも問題です。数値の定義を変更したくなったら、コードのいろいろなところを変更しなければなりません。

可能ならば、1箇所変更したら、コードのすべての部分が変更されて正常動作が保たれるようにしたいですね。

import SwiftUI

struct ContentView: View {

    var body: some View {

        VStack{

            MyText( msg: "Hello,01", font_mul: 1, font_type: 1 )
            MyText( msg: "Hello,02", font_mul: 2, font_type: 2 )
            MyText( msg: "Hello,03", font_mul: 3, font_type: 3 )
            MyText( msg: "Hello,04", font_mul: 4, font_type: 4 )

        }

    }

}

struct MyText: View {

    var msg: String
    var font_mul: Int
    var font_type: Int

    var body: some View {

        let STANDARD_SIZE = 20.0
        let font_size = STANDARD_SIZE * Double( font_mul )

        Group {

            if font_type == 1
            {
                Text( msg )
                    .font( .system(size: font_size, design: .default ))
            }
            else if font_type == 2
            {
                Text( msg )
                    .font( .system(size: font_size, design: .monospaced ))
            }
            else if font_type == 3
            {
                Text( msg )
                    .font( .system(size: font_size, design: .rounded ))
            }
            else if font_type == 4
            {
                Text( msg )
                    .font( .system(size: font_size, design: .serif ))
            }
            else
            {
                Text( msg )
                    .font( .system(size: font_size, design: .default ))
            }

        } // Group, end.

    } // some View, end.

} // View, end.

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

step1: 定義された定数を引数で渡す

上記のコードの問題点の解決策として考えつくのは定数定義です。ここでは構造体 struct に static なメンバを定義してそれに数値を割り当ててみましょう。

import SwiftUI

struct FontType {
    static let designDefault    = 1
    static let designMonospaced = 2
    static let designRounded    = 3
    static let designSerif      = 4
}

struct ContentView: View {

    var body: some View {

        VStack{

            MyText( msg: "Hello,01", font_mul: 1, font_type: FontType.designDefault )
            MyText( msg: "Hello,02", font_mul: 2, font_type: FontType.designMonospaced )
            MyText( msg: "Hello,03", font_mul: 3, font_type: FontType.designRounded )
            MyText( msg: "Hello,04", font_mul: 4, font_type: FontType.designSerif )

        }

    }

}

struct MyText: View {

    var msg: String
    var font_mul: Int
    var font_type: Int

    var body: some View {

        let STANDARD_SIZE = 20.0
        let font_size = STANDARD_SIZE * Double( font_mul )

        Group {

            if font_type == FontType.designDefault
            {
                Text( msg )
                    .font( .system(size: font_size, design: .default ))
            }
            else if font_type == FontType.designMonospaced
            {
                Text( msg )
                    .font( .system(size: font_size, design: .monospaced ))
            }
            else if font_type == FontType.designRounded
            {
                Text( msg )
                    .font( .system(size: font_size, design: .rounded ))
            }
            else if font_type == FontType.designSerif
            {
                Text( msg )
                    .font( .system(size: font_size, design: .serif ))
            }
            else
            {
                Text( msg )
                    .font( .system(size: font_size, design: .default ))
            }

        } // Group, end.

    } // some View, end.

} // View, end.

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

まあまあ良くなったのですが、なにかの間違いで16〜19行目を下記のように書いてしまってもコンパイルが成功してしまいます。

        VStack{

            MyText( msg: "Hello,01", font_mul: 1, font_type: 10 ) // コンパイルが成功してしまう.
            MyText( msg: "Hello,02", font_mul: 2, font_type: 20 ) // コンパイルが成功してしまう.
            MyText( msg: "Hello,03", font_mul: 3, font_type: 30 ) // コンパイルが成功してしまう.
            MyText( msg: "Hello,04", font_mul: 4, font_type: 40 ) // コンパイルが成功してしまう.

        }

コンストラクタの引数 font_type: の型が Int だからです。なんとかならないでしょうか。

いよいよ、ここで enum 型の出番です。

step2: 定義された enum 定数を引数で渡す

3行目を struct から enum: Int に変更しました。それに応じて31行目のコンストラクタ引数を定義された enum に変更しました。

import SwiftUI

enum FontType: Int {
    case designDefault    = 1
    case designMonospaced = 2
    case designRounded    = 3
    case designSerif      = 4
}

struct ContentView: View {

    var body: some View {

        VStack{

            MyText( msg: "Hello,01", font_mul: 1, font_type: FontType.designDefault )
            MyText( msg: "Hello,02", font_mul: 2, font_type: FontType.designMonospaced )
            MyText( msg: "Hello,03", font_mul: 3, font_type: FontType.designRounded )
            MyText( msg: "Hello,04", font_mul: 4, font_type: FontType.designSerif )

        }

    }

}

struct MyText: View {

    var msg: String
    var font_mul: Int
    var font_type: FontType

    var body: some View {

        let STANDARD_SIZE = 20.0
        let font_size = STANDARD_SIZE * Double( font_mul )

        Group {

            if font_type == FontType.designDefault
            {
                Text( msg )
                    .font( .system(size: font_size, design: .default ))
            }
            else if font_type == FontType.designMonospaced
            {
                Text( msg )
                    .font( .system(size: font_size, design: .monospaced ))
            }
            else if font_type == FontType.designRounded
            {
                Text( msg )
                    .font( .system(size: font_size, design: .rounded ))
            }
            else if font_type == FontType.designSerif
            {
                Text( msg )
                    .font( .system(size: font_size, design: .serif ))
            }
            else
            {
                Text( msg )
                    .font( .system(size: font_size, design: .default ))
            }

        } // Group, end.

    } // some View, end.

} // View, end.

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

だいぶいい感じになってきました。

ただ、enum のメンバに designDefault(0), designMonospaced(1), designRounded(2), designSerif(3) という値を割り当てている必要性を感じません。この場合は概念としての定数なので即値は不要です。最後のリファクタをします。次がいよいよ決定版です。

step3: 定義された enum 概念を引数として渡す ( 決定版 )

enum メンバに数値を割り当てるのをやめました。それ以外のコードは何も変わっていません。これが決定版です。

import SwiftUI

enum FontType {
    case designDefault
    case designMonospaced
    case designRounded
    case designSerif
}

struct ContentView: View {

    var body: some View {

        VStack{

            MyText( msg: "Hello,01", font_mul: 1, font_type: FontType.designDefault )
            MyText( msg: "Hello,02", font_mul: 2, font_type: FontType.designMonospaced )
            MyText( msg: "Hello,03", font_mul: 3, font_type: FontType.designRounded )
            MyText( msg: "Hello,04", font_mul: 4, font_type: FontType.designSerif )

        }

    }

}

struct MyText: View {

    var msg: String
    var font_mul: Int
    var font_type: FontType

    var body: some View {

        let STANDARD_SIZE = 20.0
        let font_size = STANDARD_SIZE * Double( font_mul )

        Group {

            if font_type == FontType.designDefault
            {
                Text( msg )
                    .font( .system(size: font_size, design: .default ))
            }
            else if font_type == FontType.designMonospaced
            {
                Text( msg )
                    .font( .system(size: font_size, design: .monospaced ))
            }
            else if font_type == FontType.designRounded
            {
                Text( msg )
                    .font( .system(size: font_size, design: .rounded ))
            }
            else if font_type == FontType.designSerif
            {
                Text( msg )
                    .font( .system(size: font_size, design: .serif ))
            }
            else
            {
                Text( msg )
                    .font( .system(size: font_size, design: .default ))
            }

        } // Group, end.

    } // some View, end.

} // View, end.

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}