[swift-users] Swift alternative for heterogeneous collection of objects with "generic" contents.

Joe Groff jgroff at apple.com
Fri Jun 30 11:06:57 CDT 2017


> On Jun 29, 2017, at 10:59 PM, Mikhail Seriukov via swift-users <swift-users at swift.org> wrote:
> 
> Hello everyone,
> In objc there is quite common pattern when we use a base class with a type property and concrete subclasses where type is uniquely identifying the subclass.
> We can then safely put subclasses objects into an array and then safely downcast them when needed. 
> This kind of behaviour is commonly used in data sources implementations.
> 
> Like this:
> 
> typedef NS_ENUM(NSUInteger, CellDecriptorType) {
>     CellDecriptorTypeUnknown,
>     CellDecriptorType1,
>     CellDecriptorType2
> };
> 
> @interface BaseCellDescriptor : NSObject
> 
> @property (nonatomic, readonly) CellDecriptorType type;
> @property (nonatomic, strong) id value;
> 
> @end
> 
> 
> @interface CellDescriptor1 : BaseCellDescriptor
> @end
> 
> @implementation CellDescriptor1
> 
> - (CellDecriptorType)type {
>     return CellDecriptorType1;
> }
> 
> - (NSString *)value {
> 	return @"string value";
> }
> 
> @end
> 
> 
> @interface CellDescriptor2 : BaseCellDescriptor
> @end
> 
> @implementation CellDescriptor2
> 
> - (CellDecriptorType)type {
>     return CellDecriptorType2;
> }
> 
> - (NSNumber *)value {
> 	return @42;
> }
> 
> @end
> 
> And somewhere later we do:
> 
> - (void)doWorkWithCellDescriptors:(NSArray<BaseCellDescriptor *> *)descriptors {
>     for (BaseCellDescriptor *descriptor in descriptors) {
>         CellDecriptorType type = descriptor.type;
>         switch(type) {
>             case CellDecriptorType1: {
>                 CellDescriptor1 *aDescriptor = (CellDescriptor1 *)descriptor;
>                 NSString *value = aDescriptor.value;
>                 // Do something with value
>                 break;
>             }
>             case CellDecriptorType2: {
>                 CellDescriptor2 *aDescriptor = (CellDescriptor2 *)descriptor;
>                 NSNumber *value = aDescriptor.value;
>                 // Do something with value
>                 break;
>             }
>             case CellDecriptorTypeUnknown:
>             default: {
>                 // Handle error
>                 break;
>             }
>         }
>     }
> } 
> 
> 
> I want to implement it swifty way. So the questions are:
> 0. Is it a bad practice to use this pattern and how we can avoid it?
> 1. Is it possible to avoid inheritance here and only use generic protocols and how?
> 2. Is it possible to avoid downcasting if using this pattern in swift?
> 
> 
> I've found the solution that seems to be a good example in this project https://github.com/xmartlabs/Eureka.
> They maintain both inheritance hierarchy and protocol hierarchy.

If you have a fixed set of types here, and you want to switch over them in a type-safe way, the natural thing to do would be to use an enum with payloads:

enum BaseCellDescriptor {
  case type1(CellDescriptor1)
  case type2(CellDescriptor2)
}

func doWorkWithCellDescriptors(_ descriptors: [BaseCellDescriptor]) {
  for descriptor in descriptors {
    switch descriptor {
    case .type1(let aDescriptor):
      // aDescriptor has type CellDescriptor1 here
    case .type2(let aDescriptor):
      // aDescriptor has type CellDescriptor2 here
    }
  }
}

-Joe


More information about the swift-users mailing list