web-dev-qa-db-ja.com

ネストされた列挙型でswitchステートメントをどのように使用しますか?

Moya のようなネストされた列挙型でInstagramエンドポイントの列挙型を作成しました。

enum Instagram {
    enum Media {
        case Popular
        case Shortcode(id: String)
        case Search(lat: Float, lng: Float, distance: Int)
    }
    enum Users {
        case User(id: String)
        case Feed
        case Recent(id: String)
    }
}

各エンドポイントのパスを返したいのですが。

extension Instagram: TargetType {
    var path: String {
        switch self {
        case .Media.Shortcode(let id):
            return "/media/shortcode"
        }
    }
}

ただし、上記のpathのswitchステートメントでエラーが発生します。

列挙型ケースShortcodeはタイプInstagramのメンバーではありません

直し方?

Advanced Practical Enums

18
efremidze

いくつかの理由により、より一般的な答えを追加しています。

  1. これは、ネストされた列挙型とスイッチステートメントに関する唯一の未解決の質問です。 もう一方は悲しいことに閉じています
  2. 唯一の正解は、ネストされた列挙型の値をシンボルに割り当てる方法を示していません。構文は私には直感的ではありませんでした。
  3. 他の回答には、広範な事例の例はありません。
  4. ネストされた3レベルの深さの列挙型は、必要な構文をより具体的に示しています。 efremidze回答を使用しても、解決するのにしばらく時間がかかりました。

enum Action {
    case fighter(F)
    case weapon(W)

    enum F {
        case attack(A)
        case defend(D)
        case hurt(H)

        enum A {
            case fail
            case success
        }
        enum D {
            case fail
            case success
        }
        enum H {
            case none
            case some
        }
    }
    enum W {
        case swing
        case back
    }
}

// Matches "3 deep"
let action = Action.fighter(.attack(.fail))
// Matches "1 deep" because more general case listed first.
let action2 = Action.weapon(.swing)

switch action {
case .fighter(.attack(.fail)):
    print("3 deep")
case .weapon:
    print("1 deep")
case .weapon(.swing):
    print("2 deep to case")
case .fighter(.attack):
    print("2 deep to another enum level")
default:
    print("WTF enum")
}
39
Jeff

ネストされた列挙型に関連する値を追加すると、switchステートメントを使用してそれにアクセスできます。

enum Instagram {
    enum MediaEndpoint {
        case Search(lat: Float, lng: Float, distance: Int)
    }
    case Media(MediaEndpoint)
}

extension Instagram: TargetType {
    var path: String {
        switch self {
        case .Media(.Search):
            return "/media/search"
        }
    }
}

// Demo

protocol TargetType {
    var path: String { get }
}

class MoyaProvider<Target: TargetType> {
    func request(_ target: Target, completion: @escaping () -> ()) {}
}

let provider = MoyaProvider<Instagram>()
provider.request(.Media(.Search(lat: 0, lng: 0, distance: 0))) {}
14
efremidze

アーキテクチャにはいくつかの問題があります。拡張機能とプロトコルを使用する必要がある時期と理由、およびコードブロックをどのように構造化するかを知っておく必要があります。

  1. タイプがそのプロトコルに準拠する必要がある場合は、自由に使用して独自の標準を設定してください。あなたが言及したgithubプロジェクトでそれを見さえしません。
  2. 拡張は、プリミティブ型を持ち、プロジェクトの他の部分でその機能を拡張するための良い方法です。宣言の直後に型を拡張する必要がある理由は、私には意味がありません。その良い使用例は、URLエンコードされた値をサポートするようにString型が拡張されている場合です。
private extension String {
    var URLEscapedString: String {
        return self.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet())!
    }
}
  1. このタイプのスイッチケースブロックを使用している場合
switch self {
    case .Zen:
        return "/zen"
    case .UserProfile(let name):
        return "/users/\(name.URLEscapedString)"
    case .UserRepositories(let name):
        return "/users/\(name.URLEscapedString)/repos"
}

ケースの値は、自己のメンバーである必要があります。それがタイプを見つけることができない理由です。タイプはInstagram enum内で宣言されますが、それ自体には価値がありません。 Media内で値を保持します。したがって、メディア関連の関数をMediaの宣言に移動して、そこにアクセスします。このように、自己はメディアを指します。ここに私のための完全な作業コードがあります:


private extension String {
    var URLEscapedString: String {
        return self.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet())!
    }
}


public enum Instagram {

public enum Media {
    case Search(String)

    var path:String {
        switch self {

        case Media.Search(let keyword):
            return "/media/search/\(keyword.URLEscapedString)"
        }
    }
}
</ code>

}
var me = Instagram.Media.Search( "me") print(me.path)
  1. アドバイスの一部として、アーキテクチャ全体を構築する各ステップで、そのコードがそのタイプに属しているか、またはパブリックにアクセスできるようにする必要があるかを考えてください。この場合、検索をメディアに移動することは完全に理にかなっており、メディアを検索することになります。 Userのようなものに同じパターンを追加し、異なる値を返すユーザーの下で検索することができます。
4
MKoosej

列挙型ケース検索はInstagramタイプのメンバーではありません

コンパイラが言うように、SearchはタイプInstagramのメンバーではありません。 Instagramのスコープ内の列挙型です。 SearchInstagramのインスタンスであるメンバーを作成する必要があります

struct Instagram {
    enum Media {
        case Search(lat: Float, lng: Float, distance: Int)
    }
    // something like:
     var media = .Search(lat: 0, lng: 0, distance: 0) 
    // I'm not sure this one is right syntax
    // because I can't check it right now.
    // please just get the idea
}
extension Instagram: TargetType {
    var path: String {
        switch self.media {
        case .Search(let _, let _, let _):
            return "/media/search"
        }
    }
}
1
tuledev