1

In Swift there are enums with associated values:

enum Coffee {
    case Black
    case BlackWithSugar(spoons: Int)
    // ....
}

Clearly the goal here is to:

  • Have one case that expresses BlackWithSugar (not 100 cases, like BlackWith1Sugar, BlackWith2Sugar) and so on
  • But preserve additional info (number of spoons) inside the same structure

What's the closest / most elegant way to express the same in Objective-C?

Note: I saw this question, but 2 answers do not say how to express the same intent in Objective-C. I'm ok with using something other than enum, and using Swift is not an option.

Cœur
  • 37,241
  • 25
  • 195
  • 267
timbre timbre
  • 12,648
  • 10
  • 46
  • 77
  • This blog post explains one approach. I wouldn't call it elegant. https://blog.twitch.tv/en/2018/05/14/artisanal-objective-c-sum-types-8ea1ab9da342/ – Andrew Madsen Nov 20 '19 at 17:44

1 Answers1

2

While it is possible with a lot of work to accomplish something very similar to Swift's enums with associated types in Objective-C (see the blog post I linked in a comment above), it requires a bunch of hard-to-comprehend code.

I'd recommend taking a more natural Objective-C style approach. You'll lose out on some of the type safety Swift provides, but ObjC is naturally a less type-safe language.

For example:

// Create an options enum. Bridges into Swift as an OptionSet
typedef NS_OPTIONS(NSInteger, CoffeeOptions) {
    CoffeeOptionsBlack = 0,
    CoffeeOptionsWithSugar = 1 << 0,
    CoffeeOptionsWithCream = 1 << 1
};

// Create a (non-extensible) string-typed "enum". Bridges into Swift as an enum-style struct with string "raw values"
typedef NSString *CoffeeQuantityKey NS_TYPED_EXTENSIBLE_ENUM;
static CoffeeQuantityKey const CoffeeQuantityKeySpoonsOfSugar = @"SpoonsOfSugar";
static CoffeeQuantityKey const CoffeeQuantityKeyTeaspoonsOfCream = @"TeaspoonsOfCream";

@interface CoffeeMachine : NSObject
- (void)makeCoffeeWithOptions:(CoffeeOptions)options quantities:(NSDictionary<CoffeeQuantityKey, NSNumber *> *)quantities;
@end

@implementation CoffeeMachine

- (void)makeCoffeeWithOptions:(CoffeeOptions)options quantities:(NSDictionary<CoffeeQuantityKey, NSNumber *> *)quantities
{
    // Make coffee
    if (options & CoffeeOptionsWithSugar) {
        NSNumber *sugarAmount = quantities[CoffeeQuantityKeySpoonsOfSugar] ?: @1; // Default to 1 spoon
        NSLog(@"Adding %@ spoons of sugar", sugarAmount);
    }

    if (options & CoffeeOptionsWithCream) {
        NSNumber *creamAmount = quantities[CoffeeQuantityKeyTeaspoonsOfCream] ?: @1; // Default to 1 teaspoon
        NSLog(@"Adding %@ teaspoons of cream", creamAmount);
    }
}

@end

This also has the advantage of bridging relatively nicely into Swift:

let cm = CoffeeMachine()
cm.makeCoffee(options: [.withSugar, .withCream], quantities: [.spoonsOfSugar : 3])
Andrew Madsen
  • 21,309
  • 5
  • 56
  • 97