[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