Proposal: Change Obj-C name for nested types to include enclosing types

When exposing some type to Obj-C, if the type is nested within another type, the Obj-C name should include the enclosing types.

For example:

class Foo: NSObject {
    @objc enum Bar: Int {
        case One, Two
    }
    @objc class Baz: NSObject {}
}

is currently exported to Swift as

SWIFT_CLASS("_TtC7unnamed3Foo")
@interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, Bar) {
  BarOne = 0,
  BarTwo = 1,
};

SWIFT_CLASS("_TtCC7unnamed3Foo3Baz")
@interface Baz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

I think it should be exported instead as

SWIFT_CLASS("_TtC7unnamed3Foo")
@interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, FooBar) {
  FooBarOne = 0,
  FooBarTwo = 1,
};

SWIFT_CLASS("_TtCC7unnamed3Foo3Baz")
@interface FooBaz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

This is because the Obj-C declarations are all at the top level (as Obj-C does not have nested types), so a type that is clearly unambiguous in Swift may become ambiguous in Obj-C. As a trivial example, the following Swift code:

class Foo: NSObject {
    @objc enum Bar: Int {
        case One, Two
    }
}

class Baz: NSObject {
    @objc enum Bar: Int {
        case Apple, Orange
    }
}

is currently exported to Obj-C as:

SWIFT_CLASS("_TtC7unnamed3Baz")
@interface Baz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, Bar) {
  BarApple = 0,
  BarOrange = 1,
};

SWIFT_CLASS("_TtC7unnamed3Foo")
@interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, Bar) {
  BarOne = 0,
  BarTwo = 1,
};

This is attempting to redefine the type Bar, which is of course problematic.

-Kevin Ballard

I would rather just not print nested types in the generated header. We already don't do so if the enclosing type is not Objective-C-compatible.

Jordan

···

On Dec 9, 2015, at 15:24, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

When exposing some type to Obj-C, if the type is nested within another type, the Obj-C name should include the enclosing types.

For example:

class Foo: NSObject {
   @objc enum Bar: Int {
       case One, Two
   }
   @objc class Baz: NSObject {}
}

is currently exported to Swift as

SWIFT_CLASS("_TtC7unnamed3Foo")
@interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, Bar) {
BarOne = 0,
BarTwo = 1,
};

SWIFT_CLASS("_TtCC7unnamed3Foo3Baz")
@interface Baz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

I think it should be exported instead as

SWIFT_CLASS("_TtC7unnamed3Foo")
@interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, FooBar) {
FooBarOne = 0,
FooBarTwo = 1,
};

SWIFT_CLASS("_TtCC7unnamed3Foo3Baz")
@interface FooBaz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

This is because the Obj-C declarations are all at the top level (as Obj-C does not have nested types), so a type that is clearly unambiguous in Swift may become ambiguous in Obj-C. As a trivial example, the following Swift code:

class Foo: NSObject {
   @objc enum Bar: Int {
       case One, Two
   }
}

class Baz: NSObject {
   @objc enum Bar: Int {
       case Apple, Orange
   }
}

is currently exported to Obj-C as:

SWIFT_CLASS("_TtC7unnamed3Baz")
@interface Baz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, Bar) {
BarApple = 0,
BarOrange = 1,
};

SWIFT_CLASS("_TtC7unnamed3Foo")
@interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, Bar) {
BarOne = 0,
BarTwo = 1,
};

This is attempting to redefine the type Bar, which is of course problematic.

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

We already default nested types to @nonobjc (hence the explicit @objc in my sample). I like that Swift supports using @objc on nested types, because that way I can avoid having to uglify my Swift API just for the sake of Obj-C compatibility (I know I can use a typealias inside the outer type to provide a convenient shorthand for the formerly-nested type, but I still consider that ugly).

-Kevin

···

On Wed, Dec 9, 2015, at 06:03 PM, Jordan Rose wrote:

I would rather just not print nested types in the generated header. We already don't do so if the enclosing type is not Objective-C-compatible.

Jordan

> On Dec 9, 2015, at 15:24, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:
>
> When exposing some type to Obj-C, if the type is nested within another type, the Obj-C name should include the enclosing types.
>
> For example:
>
> class Foo: NSObject {
> @objc enum Bar: Int {
> case One, Two
> }
> @objc class Baz: NSObject {}
> }
>
> is currently exported to Swift as
>
> SWIFT_CLASS("_TtC7unnamed3Foo")
> @interface Foo : NSObject
> - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
> @end
>
> typedef SWIFT_ENUM(NSInteger, Bar) {
> BarOne = 0,
> BarTwo = 1,
> };
>
> SWIFT_CLASS("_TtCC7unnamed3Foo3Baz")
> @interface Baz : NSObject
> - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
> @end
>
> I think it should be exported instead as
>
> SWIFT_CLASS("_TtC7unnamed3Foo")
> @interface Foo : NSObject
> - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
> @end
>
> typedef SWIFT_ENUM(NSInteger, FooBar) {
> FooBarOne = 0,
> FooBarTwo = 1,
> };
>
> SWIFT_CLASS("_TtCC7unnamed3Foo3Baz")
> @interface FooBaz : NSObject
> - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
> @end
>
> This is because the Obj-C declarations are all at the top level (as Obj-C does not have nested types), so a type that is clearly unambiguous in Swift may become ambiguous in Obj-C. As a trivial example, the following Swift code:
>
> class Foo: NSObject {
> @objc enum Bar: Int {
> case One, Two
> }
> }
>
> class Baz: NSObject {
> @objc enum Bar: Int {
> case Apple, Orange
> }
> }
>
> is currently exported to Obj-C as:
>
> SWIFT_CLASS("_TtC7unnamed3Baz")
> @interface Baz : NSObject
> - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
> @end
>
> typedef SWIFT_ENUM(NSInteger, Bar) {
> BarApple = 0,
> BarOrange = 1,
> };
>
> SWIFT_CLASS("_TtC7unnamed3Foo")
> @interface Foo : NSObject
> - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
> @end
>
> typedef SWIFT_ENUM(NSInteger, Bar) {
> BarOne = 0,
> BarTwo = 1,
> };
>
> This is attempting to redefine the type Bar, which is of course problematic.
>
> -Kevin Ballard
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

My point is that Swift currently treats @objc on nested types differently based on whether the outer type is @objc—if the outer type is @objc, the inner types end up in the generated header; otherwise they don't. And what about @objc on local types?

(Right now we have a hole: an API using an @objc type will show up in the generated header, but the type may not, leading to errors in the header. We should resolve that somehow.)

Jordan

···

On Dec 9, 2015, at 21:36, Kevin Ballard <kevin@sb.org> wrote:

We already default nested types to @nonobjc (hence the explicit @objc in my sample). I like that Swift supports using @objc on nested types, because that way I can avoid having to uglify my Swift API just for the sake of Obj-C compatibility (I know I can use a typealias inside the outer type to provide a convenient shorthand for the formerly-nested type, but I still consider that ugly).

-Kevin

On Wed, Dec 9, 2015, at 06:03 PM, Jordan Rose wrote:

I would rather just not print nested types in the generated header. We already don't do so if the enclosing type is not Objective-C-compatible.

Jordan

On Dec 9, 2015, at 15:24, Kevin Ballard via swift-evolution <swift-evolution@swift.org> wrote:

When exposing some type to Obj-C, if the type is nested within another type, the Obj-C name should include the enclosing types.

For example:

class Foo: NSObject {
  @objc enum Bar: Int {
      case One, Two
  }
  @objc class Baz: NSObject {}
}

is currently exported to Swift as

SWIFT_CLASS("_TtC7unnamed3Foo")
@interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, Bar) {
BarOne = 0,
BarTwo = 1,
};

SWIFT_CLASS("_TtCC7unnamed3Foo3Baz")
@interface Baz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

I think it should be exported instead as

SWIFT_CLASS("_TtC7unnamed3Foo")
@interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, FooBar) {
FooBarOne = 0,
FooBarTwo = 1,
};

SWIFT_CLASS("_TtCC7unnamed3Foo3Baz")
@interface FooBaz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

This is because the Obj-C declarations are all at the top level (as Obj-C does not have nested types), so a type that is clearly unambiguous in Swift may become ambiguous in Obj-C. As a trivial example, the following Swift code:

class Foo: NSObject {
  @objc enum Bar: Int {
      case One, Two
  }
}

class Baz: NSObject {
  @objc enum Bar: Int {
      case Apple, Orange
  }
}

is currently exported to Obj-C as:

SWIFT_CLASS("_TtC7unnamed3Baz")
@interface Baz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, Bar) {
BarApple = 0,
BarOrange = 1,
};

SWIFT_CLASS("_TtC7unnamed3Foo")
@interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

typedef SWIFT_ENUM(NSInteger, Bar) {
BarOne = 0,
BarTwo = 1,
};

This is attempting to redefine the type Bar, which is of course problematic.

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

Ah, I see what you mean.

My preference would be to print @objc types wherever they are, but
include enclosing types in the Obj-C name. This means that
something like

class Foo: NSObject { class Bar: NSObject {} }

would actually generate

@interface Foo : NSObject @end @interface FooBar : NSObject @end

In other words, the Obj-C name would default to the fully-qualified
Swift name, with the periods removed (e.g. Foo.Bar -> FooBar).

Furthermore, I think it should do this even if an enclosing type is
not @objc.

That said, I also wonder if we should have a way to explicitly hide
an @objc type from the generated header. If I'm making something
Obj-C-compatible because I need to use the Obj-C runtime, that
doesn't necessarily mean the type is actually appropriate to expose
to Obj-C code.

-Kevin Ballard

···

On Thu, Dec 10, 2015, at 04:48 PM, Jordan Rose wrote:

My point is that Swift currently treats @objc on nested types
differently based on whether the *outer* type is @objc—if the outer
type is @objc, the inner types end up in the generated header;
otherwise they don't. And what about @objc on local types?

(Right now we have a hole: an API using an @objc type will show up in
the generated header, but the type may not, leading to errors in the
header. We should resolve that somehow.)

Jordan

On Dec 9, 2015, at 21:36, Kevin Ballard <kevin@sb.org> wrote:

We already default nested types to @nonobjc (hence the explicit @objc
in my sample). I like that Swift supports using @objc on nested
types, because that way I can avoid having to uglify my Swift API
just for the sake of Obj-C compatibility (I know I can use a
typealias inside the outer type to provide a convenient shorthand for
the formerly-nested type, but I still consider that ugly).

-Kevin

On Wed, Dec 9, 2015, at 06:03 PM, Jordan Rose wrote:

I would rather just not print nested types in the generated header.
We already don't do so if the enclosing type is not Objective-C-
compatible.

Jordan

On Dec 9, 2015, at 15:24, Kevin Ballard via swift-evolution <swift- >>>> evolution@swift.org> wrote:

When exposing some type to Obj-C, if the type is nested within
another type, the Obj-C name should include the enclosing types.

For example:

class Foo: NSObject { @objc enum Bar: Int { case One, Two
} @objc class Baz: NSObject {} }

is currently exported to Swift as

SWIFT_CLASS("_TtC7unnamed3Foo") @interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER; @end

typedef SWIFT_ENUM(NSInteger, Bar) { BarOne = 0, BarTwo = 1, };

SWIFT_CLASS("_TtCC7unnamed3Foo3Baz") @interface Baz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER; @end

I think it should be exported instead as

SWIFT_CLASS("_TtC7unnamed3Foo") @interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER; @end

typedef SWIFT_ENUM(NSInteger, FooBar) { FooBarOne = 0, FooBarTwo =
1, };

SWIFT_CLASS("_TtCC7unnamed3Foo3Baz") @interface FooBaz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER; @end

This is because the Obj-C declarations are all at the top level (as
Obj-C does not have nested types), so a type that is clearly
unambiguous in Swift may become ambiguous in Obj-C. As a trivial
example, the following Swift code:

class Foo: NSObject { @objc enum Bar: Int { case One,
Two } }

class Baz: NSObject { @objc enum Bar: Int { case Apple,
Orange } }

is currently exported to Obj-C as:

SWIFT_CLASS("_TtC7unnamed3Baz") @interface Baz : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER; @end

typedef SWIFT_ENUM(NSInteger, Bar) { BarApple = 0, BarOrange =
1, };

SWIFT_CLASS("_TtC7unnamed3Foo") @interface Foo : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER; @end

typedef SWIFT_ENUM(NSInteger, Bar) { BarOne = 0, BarTwo = 1, };

This is attempting to redefine the type Bar, which is of course
problematic.

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