Generating your IGListDiffable models using remodel

With the IGListDiffable plugin for remodel by facebook, you can automatically generate models conforming to the IGListDiffable.

This will automatically implement hash, isEqual: and description, as well as diffIdentifier and isEqualToDiffableObject: for you. Remodel is also capable to generate additional code, like conforming to NSCoding or additional Builder classes for your model object. It will make creating and updating models much easier, faster and safer.

In /remodel-plugin, you can find the source files to build the IGListDiffable plugin locally.

Installation

1. Remodel installation

Please follow the installation instructions in the main remodel repository.

tl;dr: Either clone the original repository, or use an npm installation. In the latter case you can run which remodel-gen to find out the path of your installation.

2. Plugin installation

Copy the following files & folders within /remodel-plugin into your local remodel checkout:

  • /src/plugins/iglistdiffable.ts - the actual plugin
  • /src/__tests__/plugins/iglistdiffable-test.ts - unit tests
  • /features/iglistdiffable.feature - integration tests

And then register the new plugin with the system:

  • Edit /remodel/src/value-object-default-config.ts and add iglistdiffable to the list of basePlugins:
// value-object-default-config.ts
basePlugins: List.of(
    'assert-nullability',
    'assume-nonnull',
    'builder',
    'coding',
    'copying',
    'description',
    'equality',
    'fetch-status',
    'immutable-properties',
    'init-new-unavailable',
    'use-cpp',
    'iglistdiffable'
  )

3. Build plugin:

Once you copied them over and registered the plugin, you have to compile the typescript files into javascript. Do do so run this command from the remodel directory:

  • ./bin/build

4. Run tests (optional)

To run the unit/integration tests, you can run the following commands:

  • ./bin/runUnitTests
  • ./bin/runAcceptanceTests

This is especially useful if you plan to change/extend the plugin in any way.

5. Use the plugin

Now you are ready to generate your IGListDiffable conforming models! To generate a model, create a new .value file. Here’s an example:

# PersonModel.value
PersonModel includes(IGListDiffable) {
  NSString *firstName
  NSString *lastName
  %diffIdentifier
  NSString *uniqueId
}

To generate your Objective-C models, run the generation tool like this:

./bin/generate path/to/your/PersonModel.value

This will generate the following Objective-C files in the same directory:

// PersonModel.h
@interface PersonModel : NSObject <IGListDiffable, NSCopying>

@property (nonatomic, readonly, copy) NSString *firstName;
@property (nonatomic, readonly, copy) NSString *lastName;
@property (nonatomic, readonly, copy) NSString *uniqueId;

- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName uniqueId:(NSString *)uniqueId;

@end

and

// PersonModel.m
@implementation PersonModel

- (instancetype)initWithFirstName:(NSString *)firstName lastName:(NSString *)lastName uniqueId:(NSString *)uniqueId
{
  if ((self = [super init])) {
    _firstName = [firstName copy];
    _lastName = [lastName copy];
    _uniqueId = [uniqueId copy];
  }

  return self;
}

- (id<NSObject>)diffIdentifier
{
  return _uniqueId;
}

- (BOOL)isEqualToDiffableObject:(nullable id<IGListDiffable>)object
{
  return [self isEqual:object];
}

- (BOOL)isEqual:(PersonModel *)object
{
  if (self == object) {
    return YES;
  } else if (self == nil || object == nil || ![object isKindOfClass:[self class]]) {
    return NO;
  }
  return
    (_firstName == object->_firstName ? YES : [_firstName isEqual:object->_firstName]) &&
    (_lastName == object->_lastName ? YES : [_lastName isEqual:object->_lastName]) &&
    (_uniqueId == object->_uniqueId ? YES : [_uniqueId isEqual:object->_uniqueId]);
}

- (id)copyWithZone:(nullable NSZone *)zone
{
  return self;
}

- (NSString *)description
{
  return [NSString stringWithFormat:@"%@ - \n\t firstName: %@; \n\t lastName: %@; \n\t uniqueId: %@; \n", [super description], _firstName, _lastName, _uniqueId];
}

- (NSUInteger)hash
{
  NSUInteger subhashes[] = {[_firstName hash], [_lastName hash], [_uniqueId hash]};
  NSUInteger result = subhashes[0];
  for (int ii = 1; ii < 3; ++ii) {
    unsigned long long base = (((unsigned long long)result) << 32 | subhashes[ii]);
    base = (~base) + (base << 18);
    base ^= (base >> 31);
    base *=  21;
    base ^= (base >> 11);
    base += (base << 6);
    base ^= (base >> 22);
    result = base;
  }
  return result;
}

@end

Documentation

Please see the main remodel repository for additional documentation