[swift-evolution] [pitch] Make swift enum string available to Objc
Zach Waldowski
zach at waldowski.me
Mon Feb 27 15:39:08 CST 2017
-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
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/2b1e8532/attachment.html>
More information about the swift-evolution
mailing list