web-dev-qa-db-ja.com

CoreData-属性のデフォルト値として空の文字列を設定できません

現在オプションの文字列属性を持つエンティティがデータモデルにあり、これをデフォルト値の空の文字列を持つ必須属性に変換したいと思います。

他の人が発見したように、Xcode CoreDataデータモデラーでデフォルト値を空白のままにすると(設計者はこれをNULLとして解釈するため)、検証エラーが発生しますが、デフォルト値として ''、 ""、または@ ""を試行すると、これらのリテラルが発生します。必要に応じて、空の長さゼロの文字列ではなく、デフォルトとして解釈される文字。

私はGoogleで このスレッド を見つけましたが、ソリューションが本当に醜い(.xcdatamodelとobjcソースの間でモデル定義が分割されている)ことを除けば、軽量の移行では機能しません。 .xcdatamodelファイルのみに基づいて実行され、エンティティ実装のobjcロジックは読み込まれません。

データモデルデザイナでこれを実現する方法はありますか?

38
glenc

これは非常に興味深い質問です。いくつかのテストの後、データモデルのテキストフィールドが構成されている方法のため、これは不可能だと思います。

原則として、\u2205のUnicode空セット文字を使用してデフォルトの空の文字列を表すことができますが、テキストフィールドはエスケープを受け入れないようであるため、Unicode文字コードをエスケープする試みをリテラル文字列に変換しますコード文字自体の例「\ u2205」と入力すると、リテラルテキスト「\ u2205」になります。

理論的には、グラフィカルに生成された管理対象オブジェクトモデルファイルを読み込むユーティリティアプリを作成し、プログラムで属性defaultを空の文字列に設定して、ファイルをディスクに保存することができます。管理対象オブジェクトモデルファイルをコードから保存する方法が文書化されていないため、「理論上」と言います。 1つを読み取ってメモリ内で変更することはできますが、変更を永続化することはできません。

少し見落としがあると思います。

モデルが最初にロードされるときに、デフォルトの空の文字列を実用的に設定する以外に選択肢はないと思います。それは簡単ですが、醜く、(特にバージョンを移行する場合は)行ったことを覚えておく必要がありますが、現時点ではそれが唯一の選択肢だと思います。

31
TechZen

お気に入りのXMLエディター(私はEmacsを使用しました)を作成し、.xcdatamodelバンドル内の.xcdatamodeldバンドル内のcontentsファイルに飛び込みます。次に、defaultValueString=""括弧内の<attribute>...</attribute>要素に<entity>...</entity>XML属性を追加するだけです。次に例を示します。

<attribute name="email" attributeType="String" defaultValueString="" syncable="YES"/>

私はまだそうする必要がなかったので、これが移行を生き残るかどうかについて話すことができません。

16
Scott Marks

フィールドのゲッターをオーバーライドすることでこれを解決しました。nullが含まれている場合は、代わりに空の文字列を返します。

-(NSString *)unit {
    if ([self primitiveValueForKey:@"unit"] == NULL) {
        return @"";
    } else {
        return [self primitiveValueForKey:@"unit"];
    }
}

これまでのところ、それはトリックを行っているようであり、移行に影響を与えないと思います(確かに言うには十分な知識はありませんが)。結局のところ、dbにnullが存在するか空の文字列が存在するかは、フィールドを要求したときにnullではなく ""が表示される限りは気にしません。

7
Sasha

この問題を解決するための私のアプローチは、NSManagedObjectサブクラスを作成し、awakeFromInsertのNULL値の空の文字列の置換を処理することでした。次に、すべてのエンティティをNSManagedObjectの子ではなく、このサブクラスの子として設定します。ここでの前提は、特定のエンティティ内のevery文字列属性をデフォルトで空の文字列に設定することです(機能しないか、少なくとも追加のロジックが必要な場合は、同じエンティティ内でNULLのままにします)。

これを行うにはおそらくもっと効率的な方法がありますが、エンティティの作成時にのみ呼び出されるため、パフォーマンスへの影響はそれほど大きくないと思います。

- (void)awakeFromInsert {
    [super awakeFromInsert];

    NSDictionary *allAttributes = [[self entity] attributesByName];
    NSAttributeDescription *oneAttribute;

    for (NSString *oneAttributeKey in allAttributes) { 
        oneAttribute = [allAttributes objectForKey:oneAttributeKey];
        if ([oneAttribute attributeType] == NSStringAttributeType) {
            if (![self valueForKey:[oneAttribute name]]) {
                [self setValue:@"" forKey:[oneAttribute name]];
            }
        }
    }
}
6
David Ravetti

手動で行うことができます。モデルクラスで、awakeFromInsertをオーバーライドし、文字列を空の文字列に設定します

迅速:

override func awakeFromInsert()
{
    super.awakeFromInsert()
    self.stringProperty = ""
}

Objective-C

- (void) awakeFromInsert
{
    [super awakeFromInsert];
    self.stringProperty = @"";
}
3
AMTourky

これが、DavidRavettiの回答とedelaney05のコメントに基づくSwiftソリューションです。さらに、オプションチェックを追加しました。

このソリューションは私のプロジェクトではうまく機能します。

class ExampleEntity: NSManagedObject {

    ...

    override func awakeFromInsert() {
        super.awakeFromInsert()
        for (key, attr) in self.entity.attributesByName {
            if attr.attributeType == .stringAttributeType && !attr.isOptional {
                if self.value(forKey: key) == nil {
                    self.setPrimitiveValue("", forKey: key)
                }
            }
        }
    }

    ...
}
1
speshiou

構文エラーを回避するための Scott Marks answer に基づくより簡単なソリューション:

まず、here you areのように一時的にデフォルト値を見つけやすいように設定します。 .xcdatamodelバンドル内の.xcdatamodeldバンドル内のcontentsファイルを任意のテキストエディタで開きます。次に、このファイルの文字列"here you are"""に置き換えて検索を実行します。

移行は問題なく行われました。

1
Nikaaner