[swift-evolution] [pitch] Make swift enum string available to Objc

Zach Waldowski zach at waldowski.me
Mon Feb 27 16:35:35 CST 2017


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 at 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 at 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 at waldowski.me

>> 

>> 

>> On Sun, Feb 26, 2017, at 01:21 PM, Derrick Ho via swift-
>> evolution wrote:
>>> 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.
>>> 

>>> On Tue, Feb 21, 2017 at 6:09 PM Derrick Ho <wh1pch81n at 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 at 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 at 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 at swift.org

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

>>> _________________________________________________

>>> swift-evolution mailing list

>>> swift-evolution at swift.org

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

>> 

>> _______________________________________________

>>  swift-evolution mailing list

>> swift-evolution at swift.org

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

> _________________________________________________

> swift-evolution mailing list

> swift-evolution at 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170227/15d61919/attachment.html>


More information about the swift-evolution mailing list