web-dev-qa-db-ja.com

Swift 4つの新しいキーパス構文から文字列を取得しますか?

Swift 4 smart keypaths 構文(\Foo.barなど)からstring valueを取得する方法?この時点で、私はどんな方法にも興味がありますが、それが複雑であるかどうかは関係ありません。

型情報がスマートキーパスに関連付けられるというアイデアが好きです。しかし、すべてのAPIとサードパーティがまだ存在しているわけではありません。

#keyPath()によるコンパイル時検証でプロパティ名の文字列を取得する古い方法があります。 Swift 4を使用して#keyPath()を使用するには、プロパティを@objcとして宣言する必要がありますが、これは避けたいものです。

24
Dannie P

Objective-CクラスのObjective-Cプロパティには、 _kvcKeyPathString property 取得します。

ただし、Swiftキーパスには文字列に相当するものがない場合があります。Swiftキーパスは、取得するフィールドのオフセットのシーケンス、またはオブジェクトを呼び出すクロージャーとして表すことができます。

もちろん、これはプロパティの宣言を避けるというあなた自身の目的と直接衝突します@objc。私はあなたがやりたいことをするためのビルトイン機能がないと信じています。

15
zneak

簡単な答え:できません。 KeyPath 抽象化は、特定のルートタイプからの潜在的にネストされたプロパティキーパスをカプセル化するように設計されています。そのため、単一のString値をエクスポートすることは、一般的なケースでは意味がない場合があります。

たとえば、仮想的にエクスポートされた文字列は、ルートタイプのプロパティまたはそのプロパティのいずれかのメンバーとして解釈されるべきですか?このようなシナリオに対処するには、少なくとも文字列配列をエクスポートする必要があります...

タイプごとの回避策。そうは言っても、KeyPathEquatableプロトコルに準拠している場合、タイプごとにカスタムソリューションを提供できます。例えば:

struct Auth {
    var email: String
    var password: String
}
struct User {
    var name: String
    var auth: Auth
}

Userベースのキーパスにextensionを指定します。

extension PartialKeyPath where Root == User {
    var stringValue: String {
        switch self {
        case \User.name: return "name"
        case \User.auth: return "auth"
        case \User.auth.email: return "auth.email"
        case \User.auth.password: return "auth.password"
        default: fatalError("Unexpected key path")
    }
}

使用法:

let name:  KeyPath<User, String> = \User.name
let email: KeyPath<User, String> = \User.auth.email
print(name.stringValue)  /* name */
print(email.stringValue) /* auth.email */

多少のメンテナンスが必要なため、本番用のコードにはこのソリューションをお勧めしません。しかし、少なくともこの方法に興味があるので、これから先への道があります;)

23
Paulo Mattos

パーティーには少し遅れましたが、少なくともNSObjectサブクラスからキーパス文字列を取得する方法を見つけました。

NSExpression(forKeyPath: \UIView.bounds).keyPath
19
Andy Heard

これはSwift 4拡張子として機能します:

extension AnyKeyPath {
    /// Returns key path represented as a string
    var asString: String? {
        return _kvcKeyPathString?.description
    }
}

使用法:

(\UIView.bounds).asString
// or
let keyPath = \UIView.bounds
keyPath.asString
0