[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