Swiftのenumはコンパイルが通れば、本当に安全? (CGImagePropertyOrientation)

CGImagePropertyOrientation -> UIImage.Orientation に変換するコードを、ドキュメントを基に以下のように実装した。

extension UIImage.Orientation {
    init(_ cgOrientation: CGImagePropertyOrientation) {
        switch cgOrientation {
        case .up:
            self = .up
        case .upMirrored:
            self = .upMirrored
        case .down:
            self = .down
        case .downMirrored:
            self = .downMirrored
        case .left:
            self = .left
        case .leftMirrored:
            self = .leftMirrored
        case .right:
            self = .right
        case .rightMirrored:
            self = .rightMirrored
        }
    }
}

コンパイラはエラーも警告も出さない。
CGImagePropertyOrientation には @frozen が指定されており、仕様上 @unknown default も不要だ。

そう思っていた時期がありました

_人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人人_
> Fatal error: unexpected enum case 'CGImagePropertyOrientation(rawValue: 0)' <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄

CGImagePropertyOrientation

Xcode 13.2.1 (13C100) における CGImagePropertyOrientation

/* Possible int values for kCGImagePropertyTIFFOrientation */
@frozen public enum CGImagePropertyOrientation : UInt32 {

    
    case up = 1 // 0th row at top,    0th column on left   - default orientation

    case upMirrored = 2 // 0th row at top,    0th column on right  - horizontal flip

    case down = 3 // 0th row at bottom, 0th column on right  - 180 deg rotation

    case downMirrored = 4 // 0th row at bottom, 0th column on left   - vertical flip

    case leftMirrored = 5 // 0th row on left,   0th column at top

    case right = 6 // 0th row on right,  0th column at top    - 90 deg CW

    case rightMirrored = 7 // 0th row on right,  0th column on bottom

    case left = 8 // 0th row on left,   0th column at bottom - 90 deg CCW
}

CGImagePropertyOrientation の要素は固定になっているはずだが、rawValue UInt32 から変換される経路によって Fatal error が発生しているようだ。
CGImagePropertyOrientation は 1 ~ 8 の範囲を指定することを想定した設計になっているが、カメラアプリによっては今回の "0" のように、範囲外の値を指定してる可能性がある。

回避策

仕方ないので、回避策として default を埋めておくことにする。
変更しても、コンパイラからエラーと警告は出ない。

extension UIImage.Orientation {
    init(_ cgOrientation: CGImagePropertyOrientation) {
        switch cgOrientation {
        case .up:
            self = .up
        case .upMirrored:
            self = .upMirrored
        case .down:
            self = .down
        case .downMirrored:
            self = .downMirrored
        case .left:
            self = .left
        case .leftMirrored:
            self = .leftMirrored
        case .right:
            self = .right
        case .rightMirrored:
            self = .rightMirrored
        default:
            self = .up
        }
    }
}

まとめ

Swift の enum だからといって、網羅性に100%の安心ができるかは、ケースによりますね。
みなさまもお気をつけください。

それでは、よいお年を 🐮 🔜 🐯.

UIImage.Orientation -> CGImagePropertyOrientation の変換はこう

extension CGImagePropertyOrientation {
    init(_ uiOrientation: UIImage.Orientation) {
        switch uiOrientation {
        case .up:
            self = .up
        case .upMirrored:
            self = .upMirrored
        case .down:
            self = .down
        case .downMirrored:
            self = .downMirrored
        case .left:
            self = .left
        case .leftMirrored:
            self = .leftMirrored
        case .right:
            self = .right
        case .rightMirrored:
            self = .rightMirrored
        @unknown default:
            self = .up
        }
    }
}