結論
- enumにアクセス用のProtocolを適用することで可能.
検討したきっかけ
- Rxで反復して行なっているAssociated Values付きEnumへの処理を共通化したかった.
- 新メンバーのイニシャルコスト.(説明する労力、理解してもらう労力)
- 実装コスト.(数行の労力、レビューの労力)
実践
サンプル: Result型
ケース: SuccessとFailureをそれぞれに分けるオペレータを作る.
問題: Elementの型条件にResultにしたときのコンパイルエラー。
- ResultのGenericsの指定が必要.
- 具体的な型を指定すると旨味が少ない.
解決方法:
ResultType
というアクセス用プロトコルを定義しenum適用する.
以下サンプルコード.
ObservableType+Extension.swift
extension ObservableType where Element: ResultType {
func mapToSuccess() -> Observable<Element.SuccessType> {
compactMap { $0.success }
}
func mapToFailure() -> Observable<Element.FailureType> {
compactMap { $0.failure }
}
}
ResultType.swift
protocol ResultType {
associatedtype SuccessType
associatedtype FailureType: Error
var success: SuccessType? { get }
var failure: FailureType? { get }
}
Result+Extension.swift
extension Result: ResultType {
typealias SuccessType = Success
typealias FailureType = Failure
var success: Success? {
switch self {
case .success(let success):
return success
default:
return nil
}
}
var failure: Failure? {
switch self {
case .failure(let error):
return error
default:
return nil
}
}
}
おまけ
Successをさらに操作する定義など.
Barcodeよりもう少し抽象的なenumであれば実用的かも.
Omake.swift
enum Barcode {
case qr(String)
case upc(Int, Int, Int, Int)
var qrValue: String? {
switch self {
case .qr(let code):
return code
default:
return nil
}
}
var upcValue: (Int, Int, Int, Int)? {
switch self {
case .upc(let digit1, let digit2, let digit3, let digit4):
return (digit1, digit2, digit3, digit4)
default:
return nil
}
}
}
extension ObservableType where Element: ResultType, Element.SuccessType == Barcode {
func mapToQrCode() -> Observable<String> {
compactMap { $0.success?.qrValue }
}
func mapToUpc() -> Observable<(digit1: Int, digit2: Int, digit3: Int, digit4: Int)> {
compactMap { $0.success?.upcValue }
}
}