Hive: Default value within the `HiveField` annotation

Created on 15 Nov 2019  路  13Comments  路  Source: hivedb/hive

Hi!

Have an idea how the default values can be set in easy way for TypeAdapter.
All we need - add new field to HiveField annotation and then use it during class generation.

class HiveField {
  final int index;
  final dynamic defaultValue;
  const HiveField(this.index, {this.defaultValue});
}

Then in our class we can do like this:

@HiveType()
class CityHive {
  @HiveField(0, defaultValue: -1)
  int id;
  @HiveField(1, defaultValue: "Some name")
  String name;
  @HiveField(2)
  double lat;
  @HiveField(3)
  double lng;
}

Generated class will looks like this:

class CityHiveAdapter extends TypeAdapter<CityHive> {
  @override
  CityHive read(BinaryReader reader) {
    var numOfFields = reader.readByte();
    var fields = <int, dynamic>{
      for (var i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
    };
    return CityHive()
      ..id = fields[0] as int ?? -1
      ..name = fields[1] as String ?? "Some name"
      ..lat = fields[2] as double
      ..lng = fields[3] as double
  }
  ...
}
enhancement

Most helpful comment

Oh well, you're right of course. The TypeAdapter code could, of course, be like the following or something similar:

return CityHive()
      ..id = fields.containsKey(0) ? fields[0] as int : -1
      ..name = fields.containsKey(1) ? fields[1] as String : "Some name"
      ..lat = fields[2] as double
      ..lng = fields[3] as double;

All 13 comments

Hmm, what about if a field is purposely set to null?

var cities = Hive.openBox('cities');
var newYork = cities.get('ny');
newYork.name = null;
cities.put('ny', newYork);
cities.close();
cities = Hive.openBox('cities');
print(cities.get('ny')); // City has a name again

Also, when would CityHives be implicitly created? If a city doesn't exist, box.get(...) returns null anyways, so if you have code like the following, you still explicitly create a city on the caller side:

var newYork = cities.get('ny') ?? CityHive(id: ..., name: ..., ...);

Come to think of it, I could see this feature being used if you switch app versions and a new field got added.

I like this idea. It will be mandatory when non-nullable types are released and you want to add a non-nullable field to an existing object.

I like this idea. It will be mandatory when non-nullable types are released and you want to add a non-nullable field to an existing object.

Exactly. I got this idea when I was thinking about migration (adding new fields)
For example bool isFavourite, which should be false, but not null.

Hmm, what about if a field is purposely set to null?

@marcelgarus nice catch, I didn't think about it.
But i guess that field, which you mark with this annotation should not be nullable. Then it will make sense.

Hmm, what about if a field is purposely set to null?

Hive can tell the difference between a non existing field and a field set to null...

Hive can tell the difference between a non existing field and a field set to null...

how? didn't find it on documentation 馃槥

Oh well, you're right of course. The TypeAdapter code could, of course, be like the following or something similar:

return CityHive()
      ..id = fields.containsKey(0) ? fields[0] as int : -1
      ..name = fields.containsKey(1) ? fields[1] as String : "Some name"
      ..lat = fields[2] as double
      ..lng = fields[3] as double;

Bump!

I could really use this, specifically for the case where the backend schema changes and I am adding new fields to my model.

Also. If someone has a good work-around in the meantime... I struggle to find a way to handle this that I like.

I also have an alternate path to a solution I would like to open for discussion. Dart already offers default values for object properties. You could write the example at the start of this thread as:

@HiveType()
class CityHive {
  @HiveField(0)
  int id;
  @HiveField(1)
  String name = "Some name";
  @HiveField(2)
  double lat;
  @HiveField(3)
  double lng;
}

There is also defaults in the constructor you could use. The thing Hive would have to change to support this path is just to not step all over them when assigning values to the object constructor. Probably specifically when a key doesn't exist as in an earlier post in this thread. That seems like a very gentle and backwards compatible way to address this. Any property that I do not give a dart-native default value to will still return null when read.

Also. If someone has a good work-around in the meantime... I struggle to find a way to handle this that I like.

You can write custom type adapters or generate one then modify generated code.

Good call on custom type adapters.. or effectively not using generators for this.
I also just realized I can use the actual constructor in my models... not just the "shortcut assignment":

@HiveType()
class CityHive {
  @HiveField(0)
  int id;
  @HiveField(1)
  String name;
  @HiveField(2)
  double lat;
  @HiveField(3)
  double lng;

  CityHive({
    this.id,
    name,
    ... more ...
  }) {
    this.name = name ?? "Some name";
  }
}

Got into this one this week, sharing my solution in case it helps anybody:

@HiveType()
class Question {
  @HiveField(1)
  String id
  @HiveField(2)
  bool allowSomething;

  Question({
      @required this.id,
      this.allowSomething,
    }) {
      allowSomething  = allowSomething ?? false;
    }
}

Nice thing about this approach is that it uses short-hand constructor parameters & generated adapters

Got into this one this week, sharing my solution in case it helps anybody:

Thanks - I was really expecting just having fields with default values declared the normal way to work, but the principle of least astonishment was not followed here. This definitely helps, even though I don't particularly enjoy the additional boilerplate for every field I add.

I specifically wanted to add a new field and give it a default value, and this solves the problem.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Hopheylalal picture Hopheylalal  路  4Comments

ninest picture ninest  路  3Comments

NourEldinShobier picture NourEldinShobier  路  3Comments

yaymalaga picture yaymalaga  路  4Comments

maxim-saplin picture maxim-saplin  路  3Comments