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

Derrick Ho wh1pch81n at gmail.com
Mon Feb 27 16:02:44 CST 2017


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


More information about the swift-evolution mailing list