超お父さんの日記

日記とか雑談とか、スノーボードとか、サーフィンとか、たまに技術系とか。

Swift4でclass_copyPropertyListでプロパティ名が取得できない?

今作っているアプリは、AWSのMoble Hubを使っている。 バックエンド側の開発をするまでもないけど、画像アップロードしておいたり、ユーザー認証とかDB(DynamoDB)とか使いたいなという時にもってこいのサービスだ。 説明はクラスメソッドさんの記事とかがわかりやすくて良い。(いつもお世話になっております。)

そんな中で、ちょっとハマったことを覚書。
Moble Hubはめっちゃ便利で、Moble Hubにあるサービスを使う設定すると、その設定に沿ったサンプルプロジェクトが自動生成されてダウンロードできる。
そのサンプルプロジェクトを参考に実装を進めていたのだが、DynamoDBでつまづいた。
自分のプロジェクトにコードを持ってきて実行しようとしたら、以下のエラーが発生した。

Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '_deleted is not a property of MyProject.Hoge.'

エラーの発生箇所を追っていくと、AWSMTLModel.mというクラスの以下のメソッドに使われている、class_copyPropertyListというメソッドでプロパティ名の一覧が取得できていないことが原因だった。

+ (void)enumeratePropertiesUsingBlock:(void (^)(objc_property_t property, BOOL *stop))block {
    Class cls = self;
    BOOL stop = NO;

    while (!stop && ![cls isEqual:AWSMTLModel.class]) {
        unsigned count = 0;
        objc_property_t *properties = class_copyPropertyList(cls, &count);

        cls = cls.superclass;
        if (properties == NULL) continue;

        @onExit {
            free(properties);
        };

        for (unsigned i = 0; i < count; i++) {
            block(properties[i], &stop);
            if (stop) break;
        }
    }
}

プロジェクトのターゲットでSwiftのバージョンを3.2にしたら、ちゃんと動いた。
けどSwift4で実装したい。と思って、調べたら以下ようにプロパティ一覧取得したいクラスの頭に@objcMembersをつけて解決。

@objcMembers
class Hoge: AWSDynamoDBObjectModel, AWSDynamoDBModeling {
    
    var _hogeId: String?
    var _hogeDate: String?
    
    class func dynamoDBTableName() -> String {

        return "hogehoge-mobilehub-1010453911-Reply"
    }
    
    class func hashKeyAttribute() -> String {

        return "_hogeId"
    }
    
    class func rangeKeyAttribute() -> String {

        return "_hogeDate"
    }
    
    override class func jsonKeyPathsByPropertyKey() -> [AnyHashable: Any] {
        return [
               "_hogeId" : "hogeId",
               "_hogeDate" : "hogeDate",
        ]
    }
}

参考) https://bugs.swift.org/browse/SR-5748