[pitch] Make swift enum string available to Objc


(Derrick Ho) #1

Swift should not forsake objective-c. At least not when it comes enum
strings. Although swift enums are suppose to be swift only, I think we
should add a new attribute to slightly relax that. I think a good
attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be generated.

@objcstring
enum Planet {
  case Jupiter
}

I have written up a proposal with more details on what it would look for
objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this for Swift
4 stage 2


(Kevin Nattinger) #2

I don’t think we need/want to add `@objcstring` or anything like that, but I do think we should let String enums be @objc (currently it’s int types only) and imported as const refs.

// Already works
@objc enum IntEnum: Int {
    case foo = 1
    case bar = 2
}

// "not an integer type"
// Should be allowed.
@objc enum StrEnum: String {
    case foo
    case bar = "baz"
}

Becomes

// Current
typedef NS_ENUM(NSInteger, IntEnum) {
    IntEnumFoo = 1,
    IntEnumBar = 2
};

// Proposed (static or extern, depending on implementation)
NSString *const StrEnumFoo = @“foo";
NSString *const StrEnumBar = @“baz";

In fact, I’d go a step further and say any RawRepresentable enum as a globally initialized const in objc should be allowed the same treatment. Though the only other type that comes to mind is float/double.

···

On Feb 20, 2017, at 4:07 PM, Derrick Ho via swift-evolution <swift-evolution@swift.org> wrote:

Swift should not forsake objective-c. At least not when it comes enum strings. Although swift enums are suppose to be swift only, I think we should add a new attribute to slightly relax that. I think a good attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be generated.

@objcstring
enum Planet {
  case Jupiter
}

I have written up a proposal with more details on what it would look for objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this for Swift 4 stage 2
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Xiaodi Wu) #3

Agree. If technical limitations do not prohibit it, Kevin's idea seems the
more elegant.

···

On Mon, Feb 20, 2017 at 6:28 PM, Kevin Nattinger via swift-evolution < swift-evolution@swift.org> wrote:

I don’t think we need/want to add `@objcstring` or anything like that, but
I do think we should let String enums be @objc (currently it’s int types
only) and imported as const refs.

// Already works
@objc enum IntEnum: Int {
    case foo = 1
    case bar = 2
}

// "not an integer type"
// Should be allowed.
@objc enum StrEnum: String {
    case foo
    case bar = "baz"
}

Becomes

// Current
typedef NS_ENUM(NSInteger, IntEnum) {
    IntEnumFoo = 1,
    IntEnumBar = 2
};

// Proposed (static or extern, depending on implementation)
NSString *const StrEnumFoo = @“foo";
NSString *const StrEnumBar = @“baz";

In fact, I’d go a step further and say any RawRepresentable enum as a
globally initialized const in objc should be allowed the same treatment.
Though the only other type that comes to mind is float/double.

On Feb 20, 2017, at 4:07 PM, Derrick Ho via swift-evolution < > swift-evolution@swift.org> wrote:

Swift should not forsake objective-c. At least not when it comes enum
strings. Although swift enums are suppose to be swift only, I think we
should add a new attribute to slightly relax that. I think a good
attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be generated.

@objcstring
enum Planet {
  case Jupiter
}

I have written up a proposal with more details on what it would look for
objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-
enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this for Swift
4 stage 2
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Derrick Ho) #4

Swift Enum strings should not translate into objective c global static
strings.

As I wrote in my proposal

(
https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md
)

, we need to support a failable initializer.

I chose @objcstring to keep it consistent with how it would be used with
struct for NS_EXTENSIBLE_STRING_ENUM. While an enum can inherit from
String, a struct does not. So the evidence that this is meant to be used
for strings is not there. That is why I suggest @objcstring to make that
explicit and consistent between the two kinds.

···

On Mon, Feb 20, 2017 at 7:33 PM Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Agree. If technical limitations do not prohibit it, Kevin's idea seems the
more elegant.

On Mon, Feb 20, 2017 at 6:28 PM, Kevin Nattinger via swift-evolution < > swift-evolution@swift.org> wrote:

I don’t think we need/want to add `@objcstring` or anything like that, but
I do think we should let String enums be @objc (currently it’s int types
only) and imported as const refs.

// Already works
@objc enum IntEnum: Int {
    case foo = 1
    case bar = 2
}

// "not an integer type"
// Should be allowed.
@objc enum StrEnum: String {
    case foo
    case bar = "baz"
}

Becomes

// Current
typedef NS_ENUM(NSInteger, IntEnum) {
    IntEnumFoo = 1,
    IntEnumBar = 2
};

// Proposed (static or extern, depending on implementation)
NSString *const StrEnumFoo = @“foo";
NSString *const StrEnumBar = @“baz";

In fact, I’d go a step further and say any RawRepresentable enum as a
globally initialized const in objc should be allowed the same treatment.
Though the only other type that comes to mind is float/double.

On Feb 20, 2017, at 4:07 PM, Derrick Ho via swift-evolution < > swift-evolution@swift.org> wrote:

Swift should not forsake objective-c. At least not when it comes enum
strings. Although swift enums are suppose to be swift only, I think we
should add a new attribute to slightly relax that. I think a good
attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be generated.

@objcstring
enum Planet {
  case Jupiter
}

I have written up a proposal with more details on what it would look for
objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this for Swift
4 stage 2
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Michael Ilseman) #5

A quick note addressing a misconception that you’ll want to clean up for a formal proposal:

NS_[EXTENSIBLE_]STRING_ENUMs both generate Swift structs, the difference is only in the explicitness of the rawValue initializer. To use the “other direction” analogy, you’d similarly want them to apply for rawValue-ed structs alone. The reasons are the same: only the structs are layout-compatible because enums are integers.

Once you go down this route, perhaps it doesn’t make sense to annotate the struct decls themselves anymore. Maybe you just want more @objc control over bridging the types. For example, maybe you want to introduce a feature so that static members that are layout-compatible with String are bridged as global strings with the supplied name.

···

On Feb 20, 2017, at 4:07 PM, Derrick Ho via swift-evolution <swift-evolution@swift.org> wrote:

Swift should not forsake objective-c. At least not when it comes enum strings. Although swift enums are suppose to be swift only, I think we should add a new attribute to slightly relax that. I think a good attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be generated.

@objcstring
enum Planet {
  case Jupiter
}

I have written up a proposal with more details on what it would look for objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this for Swift 4 stage 2
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Derrick Ho) #6

I like how the Enum version looks

@objc enum Planet: String {}

But it looks odd for structs

@objc struct Planet: String {}

What do you think we should use?

···

On Mon, Feb 20, 2017 at 8:05 PM Derrick Ho <wh1pch81n@gmail.com> wrote:

Swift Enum strings should not translate into objective c global static
strings.

As I wrote in my proposal

(
https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md
)

, we need to support a failable initializer.

I chose @objcstring to keep it consistent with how it would be used with
struct for NS_EXTENSIBLE_STRING_ENUM. While an enum can inherit from
String, a struct does not. So the evidence that this is meant to be used
for strings is not there. That is why I suggest @objcstring to make that
explicit and consistent between the two kinds.

On Mon, Feb 20, 2017 at 7:33 PM Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

Agree. If technical limitations do not prohibit it, Kevin's idea seems the
more elegant.

On Mon, Feb 20, 2017 at 6:28 PM, Kevin Nattinger via swift-evolution < > swift-evolution@swift.org> wrote:

I don’t think we need/want to add `@objcstring` or anything like that, but
I do think we should let String enums be @objc (currently it’s int types
only) and imported as const refs.

// Already works
@objc enum IntEnum: Int {
    case foo = 1
    case bar = 2
}

// "not an integer type"
// Should be allowed.
@objc enum StrEnum: String {
    case foo
    case bar = "baz"
}

Becomes

// Current
typedef NS_ENUM(NSInteger, IntEnum) {
    IntEnumFoo = 1,
    IntEnumBar = 2
};

// Proposed (static or extern, depending on implementation)
NSString *const StrEnumFoo = @“foo";
NSString *const StrEnumBar = @“baz";

In fact, I’d go a step further and say any RawRepresentable enum as a
globally initialized const in objc should be allowed the same treatment.
Though the only other type that comes to mind is float/double.

On Feb 20, 2017, at 4:07 PM, Derrick Ho via swift-evolution < > swift-evolution@swift.org> wrote:

Swift should not forsake objective-c. At least not when it comes enum
strings. Although swift enums are suppose to be swift only, I think we
should add a new attribute to slightly relax that. I think a good
attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be generated.

@objcstring
enum Planet {
  case Jupiter
}

I have written up a proposal with more details on what it would look for
objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this for Swift
4 stage 2
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Derrick Ho) #7

True.
In my proposal I mention how NS_STRING_ENUM doesn't produce an swift enum.

So one solution is to merely "go the other way" which would produce what
Kevin N. suggested.

Is it not odd that that the objc version is so different from the swift
version?

Would it not be better to offer a somewhat similar API experience between
the two languages?

@objcstring would "promote" the swift enum to an objective-c class to make
the API experience similar.

I understand that maybe @objcstring is too niche to get its own annotation.
Instead @objc should become more powerful.

I think @objc should make an enum become a class with swift-enum like
abilities. This would allow enum functions to be bridged over to
objective-c as well.

···

On Tue, Feb 21, 2017 at 1:32 PM Michael Ilseman <milseman@apple.com> wrote:

A quick note addressing a misconception that you’ll want to clean up for a
formal proposal:

NS_[EXTENSIBLE_]STRING_ENUMs both generate Swift structs, the difference
is only in the explicitness of the rawValue initializer. To use the “other
direction” analogy, you’d similarly want them to apply for rawValue-ed
structs alone. The reasons are the same: only the structs are
layout-compatible because enums are integers.

Once you go down this route, perhaps it doesn’t make sense to annotate the
struct decls themselves anymore. Maybe you just want more @objc control
over bridging the types. For example, maybe you want to introduce a feature
so that static members that are layout-compatible with String are bridged
as global strings with the supplied name.

On Feb 20, 2017, at 4:07 PM, Derrick Ho via swift-evolution < > swift-evolution@swift.org> wrote:

Swift should not forsake objective-c. At least not when it comes enum
strings. Although swift enums are suppose to be swift only, I think we
should add a new attribute to slightly relax that. I think a good
attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be generated.

@objcstring
enum Planet {
  case Jupiter
}

I have written up a proposal with more details on what it would look for
objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this for Swift
4 stage 2

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Derrick Ho) #8

I updated my proposal
<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md>
to reflect the community's desire to build on @objc instead of adding a new
attribute @objcstring.

I've included it below for convenience:

Swift Enum strings ported to Objective-c

   - Proposal: SE-NNNN
   <https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-filename.md>
   - Authors: Derrick Ho <https://github.com/wh1pch81n>
   - Review Manager: TBD
   - Status: Awaiting review

*During the review process, add the following fields as needed:*

   - Decision Notes: Rationale
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html>
   - Previous Proposal: SE-0033
   <https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/0033-import-objc-constants.md>

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#introduction>
Introduction

We should allow swift-enum-strings and swift-struct-strings to be bridged
to objective-c. We can use the following notation:

@objc enum Planets: String { case Mercury }

@objc struct Food: String { public static let hamburger = Food(rawValue:
"hamburger") }

Creating a bridgable version will allow objective-c to enjoy some of the
benefits that swift enjoys.

Swift-evolution thread: Discussion
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html>
Discussion
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170220/032656.html>
<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#motivation>
Motivation

NS_STRING_ENUM and NS_EXSTENSIBLE_STRING_ENUM are annotations that you can
add to an objective-c global string. The annotations will make swift
interpret the objective-c global strings as enum and structs respectively
in theory. But it actually doesn't ever create enums
<https://bugs.swift.org/browse/SR-3146>.

The problem seems to stem from the conversion from objc to swift. It might
be more fruitful to make a conversion from swift to objc.

However, what if we take it a step further? Turning a swift-string-enum
into a bunch of global NSStrings really limits its power. There are many
classes written by apple that are structs in swift but become classes in
objective-c (i.e. String becomes NSString, Date becomes NSDate, Array
becomes NSArray, etc). There is a special bridging mechanism that allows
this to be possible. I think we should expand on that.
<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#proposed-solution>Proposed
solution

// `@objc` and `String` can be applied to an enum to make it available
to objective-c:
//
@objc
public enum Food: String {
   case Calamari
   case Fish
}

// This can be ported over to Objective-c as an objective-c class

@interface Food: NSObject

@property (readonly) NSString *_Nonnull rawValue;

- (instancetype _Nullable)initWithRawValue:(NSString *_Nonnull)rawValue;

+ (instanceType _Nonnull)Calamari;
+ (instanceType _Nonnull)Fish;

@end

// `@objc` and `String` can be applied to a struct to make it
available to objective-c:
//

@objc
public struct Planets: String {
  public let rawValue: String //<- This should be implicit and the
user should not need to add it
  init(rawValue: String) { self.rawValue = rawValue } //<- This should
be implicit and the user should not need to add it

  public static let Earth = Planets(rawValue: "Earth") //<- user defines these
  public static let Venus = Planets(rawValue: "Venus") //<- user defines these
}

// This can be ported over to objective-c as a class

@interface Planets: NSObject
- (instancetype _Nonnull)initWithRawValue:(NSString *_Nonnull)rawValue;

+ (instancetype)Earth;
+ (instancetype)Venus;
@end

The difference between a swift-enum-string and a swift-struct-string is
that swift-enum-string provides a failable initializer while
swift-struct-string provides a non-failable initializer.
<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#detailed-design>Detailed
design
<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum---casestring-translations>swift-string-enum
- case/string translations

A swift-enum-string, is created with cases and it has an implicit string
value based on the name of the case. The user may also add a name that does
not equal the name of the case.

// Swift
@objc
public enum Food: String {
  case Calamari
  case Fish = "Flounder" //<-- User wants Fish to be Flounder
}

// Objective-c

@interface Food: NSObject

@property (readonly) NSString *_Nonnull rawValue;

+ (instanceType _Nonnull)Calamari;
+ (instanceType _Nonnull)Fish;

@end

@implementation Food
+ (instanceType _Nonnull)Calamari { return [[Food alloc]
initWithRawValue:@"Calimari"]; }
+ (instanceType _Nonnull)Fish { return [[Food alloc]
initWithRawValue:@"Flounder"]; } //<-- Fisher contains Flounder
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum---failable-initializer>swift-string-enum
- failable initializer

A swift-enum-string has the ability to be initialized with a string. If the
string matches one of the possible cases, then it returns it, otherwise it
will return nil. This feature might be implemented as a dictionary or some
other means that gets the same results; Below is my suggestion.

// Assuming this swift implementation
@objc
public enum Food: String {
  case Calamari
  case Fish = "Flounder" //<-- User wants Fish to be Flounder
}

// The objective-c failable initializer may look like this.
@implementation Food

- (instancetype _Nullable)initWithRawValue:(NSString *_Nonnull)rawValue {
  static NSDictionary <NSString *, NSString *>*states;
  if (!states) {
    // A dictionary where the KEYs are the acceptable rawValue's and
the VALUE are empty strings
    states = @{
      @"Calimari" : @"",
      @"Flounder" : @""
    }
  }

  if ((self = [super init])) {
    if (states[rawValue]) {
      _rawValue = rawValue
      return self;
    }
  }
  return nil;
}

@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum---methods>swift-string-enum
- methods

swift enums allow methods to be defined. If you mark a method with @objc it
should be made available to objective-c.

// Swift
@objc
public enum Food: String {
  case Calamari
  case Fish

  @objc func price() -> Double {
    // ...
  }
}

// Objective-c
@interface Food: NSObject
// ...

- (Double)price;

// ...
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-struct-string---string-translations>swift-struct-string
- string translations

A swift-struct-string needs to be marked with @objc and inherit from String to
bridge to objective-c. A property or method must be marked with @objc to be
made available to objective-c.

// Swift
@objc
struct Planet {
  @objc public static let Earth = Planet(rawValue: "Earth")

  @objc public func distanceFromSun() -> Double { ... }
}

// Objective-c
@interface Planet
+ (instancetype _Nonnull)Earth;
+ (Double)distanceFromSun;
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-struct-string---non-failable-initializer>swift-struct-string
- non-failable initializer

The swift-struct-string initializer should not be failable and will accept
any string value

@implementation Planet
- (instancetype _Nonnull)initWithRawValue:(NSString *)rawValue {
  if ((self = [super init])) {
    _rawValue = rawValue;
  }

  return self;
}
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-struct-string---extension>swift-struct-string
- extension

One of the key attributes of an extensible string enum is that it can be
extended. This should produce something available to objective-c. The
original definition of Planet needs to have been marked with @objc.

// Swift
extension Planet {
  @objc public static let Pluto = Planet(rawValue: "Pluto")
}

// Objective-c

@interface Planet (extention_1)
- (instancetype _Nonnull)Pluto;
@end

@implementation Planet (extention_1)
- (instancetype _Nonnull)Pluto {
  return [[Planet alloc] initWithRawValue:@"Pluto"];
}
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum--swift-struct-string---equalityhashrawvalue>swift-string-enum
&& swift-struct-string - equality/hash/rawValue

When an enum or struct is marked with @objc and String, the objective-c
class that is produced should have its equality/hash methods and rawValue
property implicitly be implemented. The user should not need to implement
these on his/her own.

@implementation Food

- (instancetype)rawValue { return _rawValue; }

- (NSUInteger)hash {
  return [[self rawValue] hash];
}

- (BOOL)isEqual:(id)object {
  if (self == object) { return YES }
  if (![object isKindOfClass:[Food class]]) { return NO; }

  return [self.rawValue isEqualToString:((Food *)object).rawValue];
}

@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#objective-c-name>Objective-c
name

In the above examples, the objective-c name of the class and the swift name
of the class were the same. If this causes a naming conflict then the
objective-c name could be Prefixed with ENUM.

// Swift
@objc
enum Planet: String { ... }

// Objective-c
@interface ENUMPlanet
@end

The programmer should still be able to add their own name by specifying it
as an argument.

// Swift
@objc(CustomPlanet)
enum Planet { ... }

// Objective-c
@interface CustomPlanet
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#source-compatibility>Source
compatibility

This will be an additive feature and will not break anything existing.
<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#alternatives-considered>Alternatives
considered

···

-

   Implement a swift class that implements the above described behviors.
   -

   Don't change anything.

On Tue, Feb 21, 2017 at 6:09 PM Derrick Ho <wh1pch81n@gmail.com> wrote:

True.
In my proposal I mention how NS_STRING_ENUM doesn't produce an swift enum.

So one solution is to merely "go the other way" which would produce what
Kevin N. suggested.

Is it not odd that that the objc version is so different from the swift
version?

Would it not be better to offer a somewhat similar API experience between
the two languages?

@objcstring would "promote" the swift enum to an objective-c class to make
the API experience similar.

I understand that maybe @objcstring is too niche to get its own
annotation. Instead @objc should become more powerful.

I think @objc should make an enum become a class with swift-enum like
abilities. This would allow enum functions to be bridged over to
objective-c as well.
On Tue, Feb 21, 2017 at 1:32 PM Michael Ilseman <milseman@apple.com> > wrote:

A quick note addressing a misconception that you’ll want to clean up for a
formal proposal:

NS_[EXTENSIBLE_]STRING_ENUMs both generate Swift structs, the difference
is only in the explicitness of the rawValue initializer. To use the “other
direction” analogy, you’d similarly want them to apply for rawValue-ed
structs alone. The reasons are the same: only the structs are
layout-compatible because enums are integers.

Once you go down this route, perhaps it doesn’t make sense to annotate the
struct decls themselves anymore. Maybe you just want more @objc control
over bridging the types. For example, maybe you want to introduce a feature
so that static members that are layout-compatible with String are bridged
as global strings with the supplied name.

On Feb 20, 2017, at 4:07 PM, Derrick Ho via swift-evolution < > swift-evolution@swift.org> wrote:

Swift should not forsake objective-c. At least not when it comes enum
strings. Although swift enums are suppose to be swift only, I think we
should add a new attribute to slightly relax that. I think a good
attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be generated.

@objcstring
enum Planet {
  case Jupiter
}

I have written up a proposal with more details on what it would look for
objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this for Swift
4 stage 2

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Zachary Waldowski) #9

-1 as written due to the impedance mismatch with importing
NS_STRING_ENUM and NS_EXTENSIBLE_STRING_ENUM. Exporting to Objective-C
should export a typedef and several constants, not a class. Exporting
generated accessors to Objective-C is unnecessary as you have -isEqual:
and -hashValue on NSString over there.

I'm not sure how technically feasible it is to identify "a struct with a
single field conforming to RawRepresentable" to make it compatible with
@objc, though I'm not really a compiler person.

Other than that, I like the idea. I don't believe any of the annotations
to make Objective-C code better in Swift should be one-way.

Sincerely,

  Zachary Waldowski

  zach@waldowski.me

I updated my proposal[1] to reflect the community's desire to build on
@objc instead of adding a new attribute @objcstring.

I've included it below for convenience:

Swift Enum strings ported to Objective-c

* Proposal: SE-NNNN[2]
* Authors: Derrick Ho[3]
* Review Manager: TBD
* Status: Awaiting review
*During the review process, add the following fields as needed:*

* Decision Notes: Rationale[4]
* Previous Proposal: SE-0033[5]
Introduction

We should allow swift-enum-strings and swift-struct-strings to be
bridged to objective-c. We can use the following notation:
@objc enum Planets: String { case Mercury }

@objc struct Food: String { public static let hamburger =
Food(rawValue: "hamburger") }
Creating a bridgable version will allow objective-c to enjoy some of
the benefits that swift enjoys.
Swift-evolution thread: Discussion[6] Discussion[7]

Motivation

NS_STRING_ENUM and NS_EXSTENSIBLE_STRING_ENUM are annotations that you
can add to an objective-c global string. The annotations will make
swift interpret the objective-c global strings as enum and structs
respectively in theory. But it actually doesn't ever create enums[8].
The problem seems to stem from the conversion from objc to swift. It
might be more fruitful to make a conversion from swift to objc.
However, what if we take it a step further? Turning a swift-string-
enum into a bunch of global NSStrings really limits its power. There
are many classes written by apple that are structs in swift but
become classes in objective-c (i.e. String becomes NSString, Date
becomes NSDate, Array becomes NSArray, etc). There is a special
bridging mechanism that allows this to be possible. I think we should
expand on that.
Proposed solution

// `@objc` and `String` can be applied to an enum to make it available
to objective-c: // @objc public enum Food: String { case Calamari case
Fish } // This can be ported over to Objective-c as an objective-c
class @interface Food: NSObject @property (readonly) NSString
*_Nonnull rawValue; - (instancetype
_Nullable)initWithRawValue:(NSString *_Nonnull)rawValue; +
(instanceType _Nonnull)Calamari; + (instanceType _Nonnull)Fish; @end

// `@objc` and `String` can be applied to a struct to make it
available to objective-c: // @objc public struct Planets: String {
public let rawValue: String //<- This should be implicit and the user
should not need to add it init(rawValue: String) { self.rawValue =
rawValue } //<- This should be implicit and the user should not need
to add it public static let Earth = Planets(rawValue: "Earth") //<-
user defines these public static let Venus = Planets(rawValue:
"Venus") //<- user defines these } // This can be ported over to objective-
c as a class @interface Planets: NSObject - (instancetype
_Nonnull)initWithRawValue:(NSString *_Nonnull)rawValue; +
(instancetype)Earth; + (instancetype)Venus; @end

The difference between a swift-enum-string and a swift-struct-string
is that swift-enum-string provides a failable initializer while swift-struct-
string provides a non-failable initializer.
Detailed design

swift-string-enum - case/string translations

A swift-enum-string, is created with cases and it has an implicit
string value based on the name of the case. The user may also add a
name that does not equal the name of the case.

// Swift @objc public enum Food: String { case Calamari case Fish =
"Flounder" //<-- User wants Fish to be Flounder } // Objective-c
@interface Food: NSObject @property (readonly) NSString *_Nonnull
rawValue; + (instanceType _Nonnull)Calamari; + (instanceType
_Nonnull)Fish; @end @implementation Food + (instanceType
_Nonnull)Calamari { return [[Food alloc]
initWithRawValue:@"Calimari"]; } + (instanceType _Nonnull)Fish {
return [[Food alloc] initWithRawValue:@"Flounder"]; } //<-- Fisher
contains Flounder @end

swift-string-enum - failable initializer

A swift-enum-string has the ability to be initialized with a string.
If the string matches one of the possible cases, then it returns it,
otherwise it will return nil. This feature might be implemented as a
dictionary or some other means that gets the same results; Below is my
suggestion.

// Assuming this swift implementation @objc public enum Food: String {
case Calamari case Fish = "Flounder" //<-- User wants Fish to be
Flounder } // The objective-c failable initializer may look like
this. @implementation Food - (instancetype
_Nullable)initWithRawValue:(NSString *_Nonnull)rawValue { static
NSDictionary <NSString *, NSString *>*states; if (!states) { // A
dictionary where the KEYs are the acceptable rawValue's and the VALUE
are empty strings states = @{ @"Calimari" : @"", @"Flounder" : @"" } }
if ((self = [super init])) { if (states[rawValue]) { _rawValue =
rawValue return self; } } return nil; } @end

swift-string-enum - methods

swift enums allow methods to be defined. If you mark a method with
@objc it should be made available to objective-c.

// Swift @objc public enum Food: String { case Calamari case Fish
@objc func price() -> Double { // ... } } // Objective-c @interface
Food: NSObject // ... - (Double)price; // ... @end

swift-struct-string - string translations

A swift-struct-string needs to be marked with @objc and inherit from
String to bridge to objective-c. A property or method must be marked
with @objc to be made available to objective-c.

// Swift @objc struct Planet { @objc public static let Earth =
Planet(rawValue: "Earth") @objc public func distanceFromSun() ->
Double { ... } } // Objective-c @interface Planet + (instancetype
_Nonnull)Earth; + (Double)distanceFromSun; @end

swift-struct-string - non-failable initializer

The swift-struct-string initializer should not be failable and will
accept any string value

@implementation Planet - (instancetype
_Nonnull)initWithRawValue:(NSString *)rawValue { if ((self = [super
init])) { _rawValue = rawValue; } return self; } @end

swift-struct-string - extension

One of the key attributes of an extensible string enum is that it
can be extended. This should produce something available to objective-
c. The original definition of Planet needs to have been marked
with @objc.

// Swift extension Planet { @objc public static let Pluto =
Planet(rawValue: "Pluto") } // Objective-c @interface Planet
(extention_1) - (instancetype _Nonnull)Pluto; @end @implementation
Planet (extention_1) - (instancetype _Nonnull)Pluto { return [[Planet
alloc] initWithRawValue:@"Pluto"]; } @end

swift-string-enum && swift-struct-string - equality/hash/rawValue

When an enum or struct is marked with @objc and String, the objective-
c class that is produced should have its equality/hash methods and
rawValue property implicitly be implemented. The user should not need
to implement these on his/her own.

@implementation Food - (instancetype)rawValue { return _rawValue; }
- (NSUInteger)hash { return [[self rawValue] hash]; } -
(BOOL)isEqual:(id)object { if (self == object) { return YES } if
(![object isKindOfClass:[Food class]]) { return NO; } return
[self.rawValue isEqualToString:((Food *)object).rawValue]; } @end

Objective-c name

In the above examples, the objective-c name of the class and the swift
name of the class were the same. If this causes a naming conflict then
the objective-c name could be Prefixed with ENUM.

// Swift @objc enum Planet: String { ... } // Objective-c @interface
ENUMPlanet @end

The programmer should still be able to add their own name by
specifying it as an argument.

// Swift @objc(CustomPlanet) enum Planet { ... } // Objective-c
@interface CustomPlanet @end

Source compatibility

This will be an additive feature and will not break anything existing.
Alternatives considered

* Implement a swift class that implements the above described
   behviors.

* Don't change anything.

True.

In my proposal I mention how NS_STRING_ENUM doesn't produce an
swift enum.

So one solution is to merely "go the other way" which would produce
what Kevin N. suggested.

Is it not odd that that the objc version is so different from the
swift version?

Would it not be better to offer a somewhat similar API experience
between the two languages?

@objcstring would "promote" the swift enum to an objective-c class to
make the API experience similar.

I understand that maybe @objcstring is too niche to get its own
annotation. Instead @objc should become more powerful.

I think @objc should make an enum become a class with swift-enum like
abilities. This would allow enum functions to be bridged over to objective-
c as well.

A quick note addressing a misconception that you’ll want to clean up
for a formal proposal:

NS_[EXTENSIBLE_]STRING_ENUMs both generate Swift structs, the
difference is only in the explicitness of the rawValue initializer.
To use the “other direction” analogy, you’d similarly want them to
apply for rawValue-ed structs alone. The reasons are the same: only
the structs are layout-compatible because enums are integers.

Once you go down this route, perhaps it doesn’t make sense to
annotate the struct decls themselves anymore. Maybe you just want
more @objc control over bridging the types. For example, maybe you
want to introduce a feature so that static members that are layout-
compatible with String are bridged as global strings with the
supplied name.

Swift should not forsake objective-c. At least not when it comes
enum strings. Although swift enums are suppose to be swift only, I
think we should add a new attribute to slightly relax that. I
think a good attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be
generated.

@objcstring

enum Planet {

  case Jupiter

}

I have written up a proposal with more details on what it would
look for objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this
for Swift 4 stage 2
_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

Links:

  1. https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md
  2. https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-filename.md
  3. https://github.com/wh1pch81n
  4. https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html
  5. https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/0033-import-objc-constants.md
  6. https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html
  7. https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170220/032656.html
  8. https://bugs.swift.org/browse/SR-3146

···

On Sun, Feb 26, 2017, at 01:21 PM, Derrick Ho via swift-evolution wrote:

On Tue, Feb 21, 2017 at 6:09 PM Derrick Ho > <wh1pch81n@gmail.com> wrote:

On Tue, Feb 21, 2017 at 1:32 PM Michael Ilseman >> <milseman@apple.com> wrote:

On Feb 20, 2017, at 4:07 PM, Derrick Ho via swift-evolution <swift- >>>> evolution@swift.org> wrote:


(Derrick Ho) #10

NS_EXTENSIBLE_STRING_ENUM
Turns a groups of NSStrings into a struct.

What do you suggest for the reverse?

···

On Mon, Feb 27, 2017 at 4:39 PM Zach Waldowski via swift-evolution < swift-evolution@swift.org> wrote:

-1 as written due to the impedance mismatch with importing NS_STRING_ENUM
and NS_EXTENSIBLE_STRING_ENUM. Exporting to Objective-C should export a
typedef and several constants, not a class. Exporting generated accessors
to Objective-C is unnecessary as you have -isEqual: and -hashValue on
NSString over there.

I'm not sure how technically feasible it is to identify "a struct with a
single field conforming to RawRepresentable" to make it compatible with
@objc, though I'm not really a compiler person.

Other than that, I like the idea. I don't believe any of the annotations
to make Objective-C code better in Swift should be one-way.

Sincerely,
  Zachary Waldowski
  zach@waldowski.me

On Sun, Feb 26, 2017, at 01:21 PM, Derrick Ho via swift-evolution wrote:

I updated my proposal
<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md>
to reflect the community's desire to build on @objc instead of adding a new
attribute @objcstring.

I've included it below for convenience:

Swift Enum strings ported to Objective-c

   - Proposal: SE-NNNN
   <https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-filename.md>
   - Authors: Derrick Ho <https://github.com/wh1pch81n>
   - Review Manager: TBD
   - Status: Awaiting review

*During the review process, add the following fields as needed:*

   - Decision Notes: Rationale
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html>
   - Previous Proposal: SE-0033
   <https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/0033-import-objc-constants.md>

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#introduction>
Introduction

We should allow swift-enum-strings and swift-struct-strings to be bridged
to objective-c. We can use the following notation:

@objc enum Planets: String { case Mercury }

@objc struct Food: String { public static let hamburger = Food(rawValue:
"hamburger") }

Creating a bridgable version will allow objective-c to enjoy some of the
benefits that swift enjoys.

Swift-evolution thread: Discussion
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html>
Discussion
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170220/032656.html>

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#motivation>
Motivation

NS_STRING_ENUM and NS_EXSTENSIBLE_STRING_ENUM are annotations that you can
add to an objective-c global string. The annotations will make swift
interpret the objective-c global strings as enum and structs respectively
in theory. But it actually doesn't ever create enums
<https://bugs.swift.org/browse/SR-3146>.

The problem seems to stem from the conversion from objc to swift. It might
be more fruitful to make a conversion from swift to objc.

However, what if we take it a step further? Turning a swift-string-enum
into a bunch of global NSStrings really limits its power. There are many
classes written by apple that are structs in swift but become classes in
objective-c (i.e. String becomes NSString, Date becomes NSDate, Array
becomes NSArray, etc). There is a special bridging mechanism that allows
this to be possible. I think we should expand on that.

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#proposed-solution>Proposed
solution

// `@objc` and `String` can be applied to an enum to make it available to objective-c:
//
@objc
public enum Food: String {
   case Calamari
   case Fish
}

// This can be ported over to Objective-c as an objective-c class

@interface Food: NSObject

@property (readonly) NSString *_Nonnull rawValue;

- (instancetype _Nullable)initWithRawValue:(NSString *_Nonnull)rawValue;

+ (instanceType _Nonnull)Calamari;
+ (instanceType _Nonnull)Fish;

@end

// `@objc` and `String` can be applied to a struct to make it available to objective-c:
//

@objc
public struct Planets: String {
  public let rawValue: String //<- This should be implicit and the user should not need to add it
  init(rawValue: String) { self.rawValue = rawValue } //<- This should be implicit and the user should not need to add it

  public static let Earth = Planets(rawValue: "Earth") //<- user defines these
  public static let Venus = Planets(rawValue: "Venus") //<- user defines these
}

// This can be ported over to objective-c as a class

@interface Planets: NSObject
- (instancetype _Nonnull)initWithRawValue:(NSString *_Nonnull)rawValue;

+ (instancetype)Earth;
+ (instancetype)Venus;
@end

The difference between a swift-enum-string and a swift-struct-string is
that swift-enum-string provides a failable initializer while
swift-struct-string provides a non-failable initializer.

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#detailed-design>Detailed
design

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum---casestring-translations>swift-string-enum
- case/string translations

A swift-enum-string, is created with cases and it has an implicit string
value based on the name of the case. The user may also add a name that does
not equal the name of the case.

// Swift
@objc
public enum Food: String {
  case Calamari
  case Fish = "Flounder" //<-- User wants Fish to be Flounder
}

// Objective-c

@interface Food: NSObject

@property (readonly) NSString *_Nonnull rawValue;

+ (instanceType _Nonnull)Calamari;
+ (instanceType _Nonnull)Fish;

@end

@implementation Food
+ (instanceType _Nonnull)Calamari { return [[Food alloc] initWithRawValue:@"Calimari"]; }
+ (instanceType _Nonnull)Fish { return [[Food alloc] initWithRawValue:@"Flounder"]; } //<-- Fisher contains Flounder
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum---failable-initializer>swift-string-enum
- failable initializer

A swift-enum-string has the ability to be initialized with a string. If
the string matches one of the possible cases, then it returns it, otherwise
it will return nil. This feature might be implemented as a dictionary or
some other means that gets the same results; Below is my suggestion.

// Assuming this swift implementation
@objc
public enum Food: String {
  case Calamari
  case Fish = "Flounder" //<-- User wants Fish to be Flounder
}

// The objective-c failable initializer may look like this.
@implementation Food

- (instancetype _Nullable)initWithRawValue:(NSString *_Nonnull)rawValue {
  static NSDictionary <NSString *, NSString *>*states;
  if (!states) {
    // A dictionary where the KEYs are the acceptable rawValue's and the VALUE are empty strings
    states = @{
      @"Calimari" : @"",
      @"Flounder" : @""
    }
  }

  if ((self = [super init])) {
    if (states[rawValue]) {
      _rawValue = rawValue
      return self;
    }
  }
  return nil;
}

@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum---methods>swift-string-enum
- methods

swift enums allow methods to be defined. If you mark a method with @objc it
should be made available to objective-c.

// Swift
@objc
public enum Food: String {
  case Calamari
  case Fish

  @objc func price() -> Double {
    // ...
  }
}

// Objective-c
@interface Food: NSObject
// ...

- (Double)price;

// ...
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-struct-string---string-translations>swift-struct-string
- string translations

A swift-struct-string needs to be marked with @objc and inherit from
String to bridge to objective-c. A property or method must be marked with
@objc to be made available to objective-c.

// Swift
@objc
struct Planet {
  @objc public static let Earth = Planet(rawValue: "Earth")

  @objc public func distanceFromSun() -> Double { ... }
}

// Objective-c
@interface Planet
+ (instancetype _Nonnull)Earth;
+ (Double)distanceFromSun;
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-struct-string---non-failable-initializer>swift-struct-string
- non-failable initializer

The swift-struct-string initializer should not be failable and will accept
any string value

@implementation Planet
- (instancetype _Nonnull)initWithRawValue:(NSString *)rawValue {
  if ((self = [super init])) {
    _rawValue = rawValue;
  }

  return self;
}
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-struct-string---extension>swift-struct-string
- extension

One of the key attributes of an extensible string enum is that it can be
extended. This should produce something available to objective-c. The
original definition of Planet needs to have been marked with @objc.

// Swift
extension Planet {
  @objc public static let Pluto = Planet(rawValue: "Pluto")
}

// Objective-c

@interface Planet (extention_1)
- (instancetype _Nonnull)Pluto;
@end

@implementation Planet (extention_1)
- (instancetype _Nonnull)Pluto {
  return [[Planet alloc] initWithRawValue:@"Pluto"];
}
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum--swift-struct-string---equalityhashrawvalue>swift-string-enum
&& swift-struct-string - equality/hash/rawValue

When an enum or struct is marked with @objc and String, the objective-c
class that is produced should have its equality/hash methods and rawValue
property implicitly be implemented. The user should not need to implement
these on his/her own.

@implementation Food

- (instancetype)rawValue { return _rawValue; }

- (NSUInteger)hash {
  return [[self rawValue] hash];
}

- (BOOL)isEqual:(id)object {
  if (self == object) { return YES }
  if (![object isKindOfClass:[Food class]]) { return NO; }

  return [self.rawValue isEqualToString:((Food *)object).rawValue];
}

@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#objective-c-name>Objective-c
name

In the above examples, the objective-c name of the class and the swift
name of the class were the same. If this causes a naming conflict then the
objective-c name could be Prefixed with ENUM.

// Swift
@objc
enum Planet: String { ... }

// Objective-c
@interface ENUMPlanet
@end

The programmer should still be able to add their own name by specifying it
as an argument.

// Swift
@objc(CustomPlanet)
enum Planet { ... }

// Objective-c
@interface CustomPlanet
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#source-compatibility>Source
compatibility

This will be an additive feature and will not break anything existing.

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#alternatives-considered>Alternatives
considered

   -

   Implement a swift class that implements the above described behviors.
   -

   Don't change anything.

On Tue, Feb 21, 2017 at 6:09 PM Derrick Ho <wh1pch81n@gmail.com> wrote:

True.
In my proposal I mention how NS_STRING_ENUM doesn't produce an swift enum.

So one solution is to merely "go the other way" which would produce what
Kevin N. suggested.

Is it not odd that that the objc version is so different from the swift
version?

Would it not be better to offer a somewhat similar API experience between
the two languages?

@objcstring would "promote" the swift enum to an objective-c class to make
the API experience similar.

I understand that maybe @objcstring is too niche to get its own
annotation. Instead @objc should become more powerful.

I think @objc should make an enum become a class with swift-enum like
abilities. This would allow enum functions to be bridged over to
objective-c as well.
On Tue, Feb 21, 2017 at 1:32 PM Michael Ilseman <milseman@apple.com> > wrote:

A quick note addressing a misconception that you’ll want to clean up for a
formal proposal:

NS_[EXTENSIBLE_]STRING_ENUMs both generate Swift structs, the difference
is only in the explicitness of the rawValue initializer. To use the “other
direction” analogy, you’d similarly want them to apply for rawValue-ed
structs alone. The reasons are the same: only the structs are
layout-compatible because enums are integers.

Once you go down this route, perhaps it doesn’t make sense to annotate the
struct decls themselves anymore. Maybe you just want more @objc control
over bridging the types. For example, maybe you want to introduce a feature
so that static members that are layout-compatible with String are bridged
as global strings with the supplied name.

On Feb 20, 2017, at 4:07 PM, Derrick Ho via swift-evolution < > swift-evolution@swift.org> wrote:

Swift should not forsake objective-c. At least not when it comes enum
strings. Although swift enums are suppose to be swift only, I think we
should add a new attribute to slightly relax that. I think a good
attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be generated.

@objcstring
enum Planet {
  case Jupiter
}

I have written up a proposal with more details on what it would look for
objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this for Swift
4 stage 2

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

*_______________________________________________*
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Zachary Waldowski) #11

I don't understand the question. It should turn it into a group of
NSStrings prefixed by a common name. That's how they're imported from
Objective-C, stripped of their common prefix.

Sincerely,

  Zachary Waldowski

  zach@waldowski.me

NS_EXTENSIBLE_STRING_ENUM

Turns a groups of NSStrings into a struct.

What do you suggest for the reverse?

__

-1 as written due to the impedance mismatch with importing
NS_STRING_ENUM and NS_EXTENSIBLE_STRING_ENUM. Exporting to Objective-
C should export a typedef and several constants, not a class.
Exporting generated accessors to Objective-C is unnecessary as you
have -isEqual: and -hashValue on NSString over there.

I'm not sure how technically feasible it is to identify "a struct
with a single field conforming to RawRepresentable" to make it
compatible with @objc, though I'm not really a compiler person.

Other than that, I like the idea. I don't believe any of the
annotations to make Objective-C code better in Swift should be
one-way.

Sincerely,

  Zachary Waldowski

  zach@waldowski.me

I updated my proposal[1] to reflect the community's desire to build
on @objc instead of adding a new attribute @objcstring.

I've included it below for convenience:

Swift Enum strings ported to Objective-c

* Proposal: SE-NNNN[2]
* Authors: Derrick Ho[3]
* Review Manager: TBD
* Status: Awaiting review
*During the review process, add the following fields as needed:*

* Decision Notes: Rationale[4]
* Previous Proposal: SE-0033[5]
Introduction

We should allow swift-enum-strings and swift-struct-strings to be
bridged to objective-c. We can use the following notation:
@objc enum Planets: String { case Mercury }

@objc struct Food: String { public static let hamburger =
Food(rawValue: "hamburger") }
Creating a bridgable version will allow objective-c to enjoy some of
the benefits that swift enjoys.
Swift-evolution thread: Discussion[6] Discussion[7]

Motivation

NS_STRING_ENUM and NS_EXSTENSIBLE_STRING_ENUM are annotations that
you can add to an objective-c global string. The annotations will
make swift interpret the objective-c global strings as enum and
structs respectively in theory. But it actually doesn't ever create
enums[8].
The problem seems to stem from the conversion from objc to swift. It
might be more fruitful to make a conversion from swift to objc.
However, what if we take it a step further? Turning a swift-string-
enum into a bunch of global NSStrings really limits its power. There
are many classes written by apple that are structs in swift but
become classes in objective-c (i.e. String becomes NSString, Date
becomes NSDate, Array becomes NSArray, etc). There is a special
bridging mechanism that allows this to be possible. I think we
should expand on that.
Proposed solution

// `@objc` and `String` can be applied to an enum to make it
available to objective-c: // @objc public enum Food: String {
case Calamari case Fish } // This can be ported over to Objective-
c as an objective-c class @interface Food: NSObject
@property (readonly) NSString *_Nonnull rawValue; -
(instancetype _Nullable)initWithRawValue:(NSString
*_Nonnull)rawValue; + (instanceType _Nonnull)Calamari; +
(instanceType _Nonnull)Fish; @end

// `@objc` and `String` can be applied to a struct to make it
available to objective-c: // @objc public struct Planets: String {
public let rawValue: String //<- This should be implicit and the
user should not need to add it init(rawValue: String) {
self.rawValue = rawValue } //<- This should be implicit and the user
should not need to add it public static let Earth =
Planets(rawValue: "Earth") //<- user defines these public static let
Venus = Planets(rawValue: "Venus") //<- user defines these } //
This can be ported over to objective-c as a class @interface
Planets: NSObject - (instancetype
_Nonnull)initWithRawValue:(NSString *_Nonnull)rawValue; +
(instancetype)Earth; + (instancetype)Venus; @end

The difference between a swift-enum-string and a swift-struct-string
is that swift-enum-string provides a failable initializer while swift-struct-
string provides a non-failable initializer.
Detailed design

swift-string-enum - case/string translations

A swift-enum-string, is created with cases and it has an implicit
string value based on the name of the case. The user may also add a
name that does not equal the name of the case.

// Swift @objc public enum Food: String { case Calamari case Fish =
"Flounder" //<-- User wants Fish to be Flounder } // Objective-c
@interface Food: NSObject @property (readonly) NSString *_Nonnull
rawValue; + (instanceType _Nonnull)Calamari; + (instanceType
_Nonnull)Fish; @end @implementation Food + (instanceType
_Nonnull)Calamari { return [[Food alloc]
initWithRawValue:@"Calimari"]; } + (instanceType _Nonnull)Fish {
return [[Food alloc] initWithRawValue:@"Flounder"]; } //<-- Fisher
contains Flounder @end

swift-string-enum - failable initializer

A swift-enum-string has the ability to be initialized with a string.
If the string matches one of the possible cases, then it returns it,
otherwise it will return nil. This feature might be implemented as a
dictionary or some other means that gets the same results; Below is
my suggestion.

// Assuming this swift implementation @objc public enum Food: String
{ case Calamari case Fish = "Flounder" //<-- User wants Fish to be
Flounder } // The objective-c failable initializer may look like
this. @implementation Food - (instancetype
_Nullable)initWithRawValue:(NSString *_Nonnull)rawValue { static
NSDictionary <NSString *, NSString *>*states; if (!states) { // A
dictionary where the KEYs are the acceptable rawValue's and the
VALUE are empty strings states = @{ @"Calimari" : @"", @"Flounder" :
@"" } } if ((self = [super init])) { if (states[rawValue]) {
_rawValue = rawValue return self; } } return nil; } @end

swift-string-enum - methods

swift enums allow methods to be defined. If you mark a method with
@objc it should be made available to objective-c.

// Swift @objc public enum Food: String { case Calamari case Fish
@objc func price() -> Double { // ... } } // Objective-c @interface
Food: NSObject // ... - (Double)price; // ... @end

swift-struct-string - string translations

A swift-struct-string needs to be marked with @objc and inherit from
String to bridge to objective-c. A property or method must be marked
with @objc to be made available to objective-c.

// Swift @objc struct Planet { @objc public static let Earth =
Planet(rawValue: "Earth") @objc public func distanceFromSun() ->
Double { ... } } // Objective-c @interface Planet + (instancetype
_Nonnull)Earth; + (Double)distanceFromSun; @end

swift-struct-string - non-failable initializer

The swift-struct-string initializer should not be failable and will
accept any string value

@implementation Planet - (instancetype
_Nonnull)initWithRawValue:(NSString *)rawValue { if ((self = [super
init])) { _rawValue = rawValue; } return self; } @end

swift-struct-string - extension

One of the key attributes of an extensible string enum is that it
can be extended. This should produce something available to objective-
c. The original definition of Planet needs to have been marked with
@objc.

// Swift extension Planet { @objc public static let Pluto =
Planet(rawValue: "Pluto") } // Objective-c @interface Planet
(extention_1) - (instancetype _Nonnull)Pluto; @end @implementation
Planet (extention_1) - (instancetype _Nonnull)Pluto { return
[[Planet alloc] initWithRawValue:@"Pluto"]; } @end

swift-string-enum && swift-struct-string - equality/hash/rawValue
When an enum or struct is marked with @objc and String, the objective-
c class that is produced should have its equality/hash methods and
rawValue property implicitly be implemented. The user should not
need to implement these on his/her own.

@implementation Food - (instancetype)rawValue { return _rawValue; }
- (NSUInteger)hash { return [[self rawValue] hash]; } -
(BOOL)isEqual:(id)object { if (self == object) { return YES } if
(![object isKindOfClass:[Food class]]) { return NO; } return
[self.rawValue isEqualToString:((Food *)object).rawValue]; } @end

Objective-c name

In the above examples, the objective-c name of the class and the
swift name of the class were the same. If this causes a naming
conflict then the objective-c name could be Prefixed with ENUM.

// Swift @objc enum Planet: String { ... } // Objective-c
@interface ENUMPlanet @end

The programmer should still be able to add their own name by
specifying it as an argument.

// Swift @objc(CustomPlanet) enum Planet { ... } // Objective-c
@interface CustomPlanet @end

Source compatibility

This will be an additive feature and will not break anything
existing.
Alternatives considered

* Implement a swift class that implements the above described
   behviors.

* Don't change anything.

True.

In my proposal I mention how NS_STRING_ENUM doesn't produce an
swift enum.

So one solution is to merely "go the other way" which would produce
what Kevin N. suggested.

Is it not odd that that the objc version is so different from the
swift version?

Would it not be better to offer a somewhat similar API experience
between the two languages?

@objcstring would "promote" the swift enum to an objective-c class
to make the API experience similar.

I understand that maybe @objcstring is too niche to get its own
annotation. Instead @objc should become more powerful.

I think @objc should make an enum become a class with swift-enum
like abilities. This would allow enum functions to be bridged over
to objective-c as well.

A quick note addressing a misconception that you’ll want to clean
up for a formal proposal:

NS_[EXTENSIBLE_]STRING_ENUMs both generate Swift structs, the
difference is only in the explicitness of the rawValue
initializer. To use the “other direction” analogy, you’d similarly
want them to apply for rawValue-ed structs alone. The reasons are
the same: only the structs are layout-compatible because enums are
integers.

Once you go down this route, perhaps it doesn’t make sense to
annotate the struct decls themselves anymore. Maybe you just want
more @objc control over bridging the types. For example, maybe you
want to introduce a feature so that static members that are layout-
compatible with String are bridged as global strings with the
supplied name.

Swift should not forsake objective-c. At least not when it comes
enum strings. Although swift enums are suppose to be swift only,
I think we should add a new attribute to slightly relax that. I
think a good attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be
generated.

@objcstring

enum Planet {

  case Jupiter

}

I have written up a proposal with more details on what it would
look for objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this
for Swift 4 stage 2
_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

_________________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

Links:

  1. https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md
  2. https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-filename.md
  3. https://github.com/wh1pch81n
  4. https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html
  5. https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/0033-import-objc-constants.md
  6. https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html
  7. https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170220/032656.html
  8. https://bugs.swift.org/browse/SR-3146

···

On Mon, Feb 27, 2017, at 05:02 PM, Derrick Ho via swift-evolution wrote:

On Mon, Feb 27, 2017 at 4:39 PM Zach Waldowski via swift-evolution <swift- > evolution@swift.org> wrote:

On Sun, Feb 26, 2017, at 01:21 PM, Derrick Ho via swift- >> evolution wrote:

On Tue, Feb 21, 2017 at 6:09 PM Derrick Ho <wh1pch81n@gmail.com> >>> wrote:

On Tue, Feb 21, 2017 at 1:32 PM Michael Ilseman >>>> <milseman@apple.com> wrote:

On Feb 20, 2017, at 4:07 PM, Derrick Ho via swift-evolution <swift- >>>>>> evolution@swift.org> wrote:


(Derrick Ho) #12

Thank you all for your input, I have revised the proposal to merely "go the
other way". It will allow Swift API developers to make enum strings
available to objective-c as global strings.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

Swift Enum strings ported to Objective-c

   - Proposal: SE-NNNN
   <https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-filename.md>
   - Authors: Derrick Ho <https://github.com/wh1pch81n>
   - Review Manager: TBD
   - Status: Awaiting review

*During the review process, add the following fields as needed:*

   - Decision Notes: Rationale
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html>
   - Previous Proposal: SE-0033
   <https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/0033-import-objc-constants.md>

<https://github.com/wh1pch81n/swift-evolution/tree/swift-enum-objc#introduction>
Introduction

We should allow swift-enum-strings and swift-struct-strings to be ported to
objective-c. We can use the following notation:

@objc enum Planets: String { case Mercury }

struct Food { @objc public static let hamburger = Food(rawValue:
"hamburger") }

Swift-evolution thread: Discussion
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html>
Discussion
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170220/032656.html>
<https://github.com/wh1pch81n/swift-evolution/tree/swift-enum-objc#motivation>
Motivation

NS_STRING_ENUM and NS_EXSTENSIBLE_STRING_ENUM are annotations that you can
add to an objective-c global string. The annotations will make swift
interpret the objective-c global strings as structs. However, there does
not exist any way to go the other way around. Anyone that wants global
strings to be available to objective-c and swift will need to make them in
Objective-c. We should give API authors the ability to make global strings
in the swift language by allowing enums to be converted to NSString's in
objective-c
<https://github.com/wh1pch81n/swift-evolution/tree/swift-enum-objc#proposed-solution>Proposed
solution

// `@objc` and `String` can be applied to an enum to make it available
to objective-c:
//
@objc
public enum Food: String {
   case Calamari
   case Fish
}

// This can be ported over to Objective-c as an objective-c class

typedef NSString *_Nonnull Food;
static Food const FoodCalamari = @"Calamari";
static Food const FoodFish = @"Fish";

// `@objc` and `String` can be applied to a struct to make it
available to objective-c:
//

@objc
public struct Planets: String {
  public let rawValue: String //<- This should be implicit and the
user should not need to add it
  init(rawValue: String) { self.rawValue = rawValue } //<- This should
be implicit and the user should not need to add it

  @objc public static let Earth = Planets(rawValue: "Earth") //<- user
defines these
  @objc public static let Venus = Planets(rawValue: "Venus") //<- user
defines these
}

// This can be ported over to objective-c as a class

typedef NSString *_Nonnull Planets;
Planets const PlanetsEarth = @"Earth";
Planets const PlanetsVenus = @"Venus";

<https://github.com/wh1pch81n/swift-evolution/tree/swift-enum-objc#detailed-design>Detailed
design
<https://github.com/wh1pch81n/swift-evolution/tree/swift-enum-objc#swift-string-enum---casestring-translations>swift-string-enum
- case/string translations

A swift-enum-string, is created with cases and it has an implicit string
value based on the name of the case. The user may also add a name that does
not equal the name of the case.

// Swift
@objc
public enum Food: String {
  case Calamari
  case Fish = "Flounder" //<-- User wants Fish to be Flounder
}

// Objective-c
// The variable will <name_of_enum><name_of_case>. And should follow
camel-case rules.
typedef NSString *_Nonnull Food;
static Food const FoodCalamari = @"Calamari";
static Food const FoodFish = @"Flounder";

<https://github.com/wh1pch81n/swift-evolution/tree/swift-enum-objc#swift-struct-string---string-translations>swift-struct-string
- string translations

A swift-struct-string needs to be marked with @objc and inherit from String to
be available to objective-c. A property must be marked with @objc to be
made available to objective-c.

// Swift
@objc
struct Planet {
  @objc public static let Earth = Planet(rawValue: "Earth")
}

// Objective-c
typedef NSString *_Nonnull Planet;
static Planet const PlanetEarth = @"Earth";

<https://github.com/wh1pch81n/swift-evolution/tree/swift-enum-objc#source-compatibility>Source
compatibility

This will be an additive feature and will not break anything existing.
<https://github.com/wh1pch81n/swift-evolution/tree/swift-enum-objc#alternatives-considered>Alternatives
considered

   - Don't change anything.

···

On Mon, Feb 27, 2017 at 5:35 PM Zach Waldowski via swift-evolution < swift-evolution@swift.org> wrote:

I don't understand the question. It should turn it into a group of
NSStrings prefixed by a common name. That's how they're imported from
Objective-C, stripped of their common prefix.

Sincerely,
  Zachary Waldowski
  zach@waldowski.me

On Mon, Feb 27, 2017, at 05:02 PM, Derrick Ho via swift-evolution wrote:

NS_EXTENSIBLE_STRING_ENUM
Turns a groups of NSStrings into a struct.

What do you suggest for the reverse?
On Mon, Feb 27, 2017 at 4:39 PM Zach Waldowski via swift-evolution < > swift-evolution@swift.org> wrote:

-1 as written due to the impedance mismatch with importing NS_STRING_ENUM
and NS_EXTENSIBLE_STRING_ENUM. Exporting to Objective-C should export a
typedef and several constants, not a class. Exporting generated accessors
to Objective-C is unnecessary as you have -isEqual: and -hashValue on
NSString over there.

I'm not sure how technically feasible it is to identify "a struct with a
single field conforming to RawRepresentable" to make it compatible with
@objc, though I'm not really a compiler person.

Other than that, I like the idea. I don't believe any of the annotations
to make Objective-C code better in Swift should be one-way.

Sincerely,
  Zachary Waldowski
  zach@waldowski.me

On Sun, Feb 26, 2017, at 01:21 PM, Derrick Ho via swift-evolution wrote:

I updated my proposal
<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md>
to reflect the community's desire to build on @objc instead of adding a new
attribute @objcstring.

I've included it below for convenience:

Swift Enum strings ported to Objective-c

   - Proposal: SE-NNNN
   <https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-filename.md>
   - Authors: Derrick Ho <https://github.com/wh1pch81n>
   - Review Manager: TBD
   - Status: Awaiting review

*During the review process, add the following fields as needed:*

   - Decision Notes: Rationale
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html>
   - Previous Proposal: SE-0033
   <https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/0033-import-objc-constants.md>

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#introduction>
Introduction

We should allow swift-enum-strings and swift-struct-strings to be bridged
to objective-c. We can use the following notation:

@objc enum Planets: String { case Mercury }

@objc struct Food: String { public static let hamburger = Food(rawValue:
"hamburger") }

Creating a bridgable version will allow objective-c to enjoy some of the
benefits that swift enjoys.

Swift-evolution thread: Discussion
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20161114/028950.html>
Discussion
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170220/032656.html>

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#motivation>
Motivation

NS_STRING_ENUM and NS_EXSTENSIBLE_STRING_ENUM are annotations that you can
add to an objective-c global string. The annotations will make swift
interpret the objective-c global strings as enum and structs respectively
in theory. But it actually doesn't ever create enums
<https://bugs.swift.org/browse/SR-3146>.

The problem seems to stem from the conversion from objc to swift. It might
be more fruitful to make a conversion from swift to objc.

However, what if we take it a step further? Turning a swift-string-enum
into a bunch of global NSStrings really limits its power. There are many
classes written by apple that are structs in swift but become classes in
objective-c (i.e. String becomes NSString, Date becomes NSDate, Array
becomes NSArray, etc). There is a special bridging mechanism that allows
this to be possible. I think we should expand on that.

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#proposed-solution>Proposed
solution

// `@objc` and `String` can be applied to an enum to make it available to objective-c:
//
@objc
public enum Food: String {
   case Calamari
   case Fish
}

// This can be ported over to Objective-c as an objective-c class

@interface Food: NSObject

@property (readonly) NSString *_Nonnull rawValue;

- (instancetype _Nullable)initWithRawValue:(NSString *_Nonnull)rawValue;

+ (instanceType _Nonnull)Calamari;
+ (instanceType _Nonnull)Fish;

@end

// `@objc` and `String` can be applied to a struct to make it available to objective-c:
//

@objc
public struct Planets: String {
  public let rawValue: String //<- This should be implicit and the user should not need to add it
  init(rawValue: String) { self.rawValue = rawValue } //<- This should be implicit and the user should not need to add it

  public static let Earth = Planets(rawValue: "Earth") //<- user defines these
  public static let Venus = Planets(rawValue: "Venus") //<- user defines these
}

// This can be ported over to objective-c as a class

@interface Planets: NSObject
- (instancetype _Nonnull)initWithRawValue:(NSString *_Nonnull)rawValue;

+ (instancetype)Earth;
+ (instancetype)Venus;
@end

The difference between a swift-enum-string and a swift-struct-string is
that swift-enum-string provides a failable initializer while
swift-struct-string provides a non-failable initializer.

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#detailed-design>Detailed
design

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum---casestring-translations>swift-string-enum
- case/string translations

A swift-enum-string, is created with cases and it has an implicit string
value based on the name of the case. The user may also add a name that does
not equal the name of the case.

// Swift
@objc
public enum Food: String {
  case Calamari
  case Fish = "Flounder" //<-- User wants Fish to be Flounder
}

// Objective-c

@interface Food: NSObject

@property (readonly) NSString *_Nonnull rawValue;

+ (instanceType _Nonnull)Calamari;
+ (instanceType _Nonnull)Fish;

@end

@implementation Food
+ (instanceType _Nonnull)Calamari { return [[Food alloc] initWithRawValue:@"Calimari"]; }
+ (instanceType _Nonnull)Fish { return [[Food alloc] initWithRawValue:@"Flounder"]; } //<-- Fisher contains Flounder
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum---failable-initializer>swift-string-enum
- failable initializer

A swift-enum-string has the ability to be initialized with a string. If
the string matches one of the possible cases, then it returns it, otherwise
it will return nil. This feature might be implemented as a dictionary or
some other means that gets the same results; Below is my suggestion.

// Assuming this swift implementation
@objc
public enum Food: String {
  case Calamari
  case Fish = "Flounder" //<-- User wants Fish to be Flounder
}

// The objective-c failable initializer may look like this.
@implementation Food

- (instancetype _Nullable)initWithRawValue:(NSString *_Nonnull)rawValue {
  static NSDictionary <NSString *, NSString *>*states;
  if (!states) {
    // A dictionary where the KEYs are the acceptable rawValue's and the VALUE are empty strings
    states = @{
      @"Calimari" : @"",
      @"Flounder" : @""
    }
  }

  if ((self = [super init])) {
    if (states[rawValue]) {
      _rawValue = rawValue
      return self;
    }
  }
  return nil;
}

@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum---methods>swift-string-enum
- methods

swift enums allow methods to be defined. If you mark a method with @objc it
should be made available to objective-c.

// Swift
@objc
public enum Food: String {
  case Calamari
  case Fish

  @objc func price() -> Double {
    // ...
  }
}

// Objective-c
@interface Food: NSObject
// ...

- (Double)price;

// ...
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-struct-string---string-translations>swift-struct-string
- string translations

A swift-struct-string needs to be marked with @objc and inherit from
String to bridge to objective-c. A property or method must be marked with
@objc to be made available to objective-c.

// Swift
@objc
struct Planet {
  @objc public static let Earth = Planet(rawValue: "Earth")

  @objc public func distanceFromSun() -> Double { ... }
}

// Objective-c
@interface Planet
+ (instancetype _Nonnull)Earth;
+ (Double)distanceFromSun;
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-struct-string---non-failable-initializer>swift-struct-string
- non-failable initializer

The swift-struct-string initializer should not be failable and will accept
any string value

@implementation Planet
- (instancetype _Nonnull)initWithRawValue:(NSString *)rawValue {
  if ((self = [super init])) {
    _rawValue = rawValue;
  }

  return self;
}
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-struct-string---extension>swift-struct-string
- extension

One of the key attributes of an extensible string enum is that it can be
extended. This should produce something available to objective-c. The
original definition of Planet needs to have been marked with @objc.

// Swift
extension Planet {
  @objc public static let Pluto = Planet(rawValue: "Pluto")
}

// Objective-c

@interface Planet (extention_1)
- (instancetype _Nonnull)Pluto;
@end

@implementation Planet (extention_1)
- (instancetype _Nonnull)Pluto {
  return [[Planet alloc] initWithRawValue:@"Pluto"];
}
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#swift-string-enum--swift-struct-string---equalityhashrawvalue>swift-string-enum
&& swift-struct-string - equality/hash/rawValue

When an enum or struct is marked with @objc and String, the objective-c
class that is produced should have its equality/hash methods and rawValue
property implicitly be implemented. The user should not need to implement
these on his/her own.

@implementation Food

- (instancetype)rawValue { return _rawValue; }

- (NSUInteger)hash {
  return [[self rawValue] hash];
}

- (BOOL)isEqual:(id)object {
  if (self == object) { return YES }
  if (![object isKindOfClass:[Food class]]) { return NO; }

  return [self.rawValue isEqualToString:((Food *)object).rawValue];
}

@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#objective-c-name>Objective-c
name

In the above examples, the objective-c name of the class and the swift
name of the class were the same. If this causes a naming conflict then the
objective-c name could be Prefixed with ENUM.

// Swift
@objc
enum Planet: String { ... }

// Objective-c
@interface ENUMPlanet
@end

The programmer should still be able to add their own name by specifying it
as an argument.

// Swift
@objc(CustomPlanet)
enum Planet { ... }

// Objective-c
@interface CustomPlanet
@end

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#source-compatibility>Source
compatibility

This will be an additive feature and will not break anything existing.

<https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md#alternatives-considered>Alternatives
considered

   -

   Implement a swift class that implements the above described behviors.
   -

   Don't change anything.

On Tue, Feb 21, 2017 at 6:09 PM Derrick Ho <wh1pch81n@gmail.com> wrote:

True.
In my proposal I mention how NS_STRING_ENUM doesn't produce an swift enum.

So one solution is to merely "go the other way" which would produce what
Kevin N. suggested.

Is it not odd that that the objc version is so different from the swift
version?

Would it not be better to offer a somewhat similar API experience between
the two languages?

@objcstring would "promote" the swift enum to an objective-c class to make
the API experience similar.

I understand that maybe @objcstring is too niche to get its own
annotation. Instead @objc should become more powerful.

I think @objc should make an enum become a class with swift-enum like
abilities. This would allow enum functions to be bridged over to
objective-c as well.
On Tue, Feb 21, 2017 at 1:32 PM Michael Ilseman <milseman@apple.com> > wrote:

A quick note addressing a misconception that you’ll want to clean up for a
formal proposal:

NS_[EXTENSIBLE_]STRING_ENUMs both generate Swift structs, the difference
is only in the explicitness of the rawValue initializer. To use the “other
direction” analogy, you’d similarly want them to apply for rawValue-ed
structs alone. The reasons are the same: only the structs are
layout-compatible because enums are integers.

Once you go down this route, perhaps it doesn’t make sense to annotate the
struct decls themselves anymore. Maybe you just want more @objc control
over bridging the types. For example, maybe you want to introduce a feature
so that static members that are layout-compatible with String are bridged
as global strings with the supplied name.

On Feb 20, 2017, at 4:07 PM, Derrick Ho via swift-evolution < > swift-evolution@swift.org> wrote:

Swift should not forsake objective-c. At least not when it comes enum
strings. Although swift enums are suppose to be swift only, I think we
should add a new attribute to slightly relax that. I think a good
attribute would be @objcstring.

By adding @objcstring, an objective-c exclusive class will be generated.

@objcstring
enum Planet {
  case Jupiter
}

I have written up a proposal with more details on what it would look for
objective-c.

https://github.com/wh1pch81n/swift-evolution/blob/swift-enum-objc/proposals/NNNN-Swift-enum-strings-ported-to-Objective-c.md

If no one objects to this proposal I'll submit it.

**notes: I am reviving this discussion so that I may submit this for Swift
4 stage 2

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

*_______________________________________________*
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

*_______________________________________________*
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution