SE-0385: Custom Reflection Metadata

The concrete things Realm would use this for are:

  1. Automatic model class discovery. If the user does not supply a schema when opening a database, we currently call objc_copyClassList() to find all subclasses of RealmSwift.Object (and some other things) and generate a schema from them. objc_copyClassList() has several issues, but the big functional problem is that it eagerly loads all linked dylibs. This hurts app startup time, increases general app memory usage, and can make extensions overshoot their memory budget by itself.

    This proposal gives us a way to get automatic schema discovery without most of the drawbacks, and in a way that can probably be deployed as a non-breaking change to our users (the only thing I could see being a problem is if the Reflection module has a hard requirement on a deployment target instead of requiring @available guards for some reason).

  2. Advanced schema customization. For example, a property @Persisted var foo: Int creates an integer column named "foo" in the underlying database table. We'd like to support something like @Persisted(named: "bar") var foo: Int or @Named("bar") @Persisted var foo: Int to let the user specify that the underlying column should instead be named "bar". Doing this via a property wrapper requires storing the string "bar" on every instance of the object and not just the one instantiated for schema discovery, which is an unacceptable increase in memory usage.

    We currently support doing this by overriding a class method that returns a dictionary of name mappings, which is not a very good API.

I don't think we'd use this proposal for anything else by itself. The problem with using it to avoid instantiating an object, using Mirror to slurp the properties, and then using ivar_getOffset to work around the lack of keypaths on Mirror is that we would still need the @Persisted property wrapper. It does some wacky things for schema discovery that could go away, but it's also just the thing that turns property accesses into database reads. This means that we'd need property declarations to be something like @Discoverable @Persisted var foo: Int, i.e. we'd be making our API worse and requiring users to write more boilerplate just to make our implementation simpler.

It's possible that declaration macros fix this. We could perhaps have a single macro attribute that applies both required attributes to the property (or fully expands into getters and setters and skips the property wrapper entirely).


My opinion on the proposal is still roughly what it was for the pitch: I'm not wild about this specific design, but it is something we'd use if it existed and it'd solve both functional and API design issues we're currently facing.

3 Likes