Discrepancy between sharing internal Swift symbols with Obj-C when in an app vs a framework target


(Kevin Lundberg) #1

I ran into a major hurdle this week that basically stopped my work in
its tracks. I've been working on moving a large codebase from an iOS app
target to a framework target, since we have the same code in multiple
app targets and it is problematic to have to remember to add new code to
every single app target when they can all just share a framework.

However I didn't anticipate the issue that made this task explode in
terms of effort. According to the Using Swift with Cocoa & obj-C book
(this section:
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html#//apple_ref/doc/uid/TP40014216-CH10-ID122),
swift code in an app target can be accessed from obj-c if it is internal
or public. However, swift code in a framework target can only be
accessed from objc if it is public, not internal.

Is there any way around this restriction? The codebase I want to migrate
has a lot of swift and obj-c intermingling, and I can't see a reasonable
end to converting everything I need to convert to public from internal
for this to work.

If there's no feasible workaround, would swift-evolution be the proper
place to discuss changing this behavior? Or is this something that would
need to be logged in radar for the Xcode team to address?

Thanks,
- Kevin


(Jens Alfke) #2

swift code in an app target can be accessed from obj-c if it is internal
or public. However, swift code in a framework target can only be
accessed from objc if it is public, not internal.

That’s exactly what the access modifiers are supposed to do. A framework is by definition a separate package from the binary that imports it, so only public symbols are visible.

This doesn’t only apply to Obj-C code, either. Swift code in your app won’t be able to use internal APIs of Swift code from the framework, either.

would swift-evolution be the proper place to discuss changing this behavior?

This seems to be pretty clearly the correct behavior, so I can’t see it being changed.

—Jens

···

On Jun 3, 2016, at 7:55 PM, Kevin Lundberg via swift-users <swift-users@swift.org> wrote:


(Brent Royal-Gordon) #3

I ran into a major hurdle this week that basically stopped my work in
its tracks. I've been working on moving a large codebase from an iOS app
target to a framework target, since we have the same code in multiple
app targets and it is problematic to have to remember to add new code to
every single app target when they can all just share a framework.

To be clear: Are you having trouble making the Objective-C and Swift inside your framework talk to each other, or the Objective-C outside your framework talk to the Swift inside your framework?

If it's the latter, then I agree with Jens that this is "works as intended", and you're just going to have to spend some time pasting `public` into your code in a lot of places. But if you're being forced to make Swift APIs public so you can use them from Objective-C *inside* the framework, that might be something worth talking about.

···

--
Brent Royal-Gordon
Architechies


(Kevin Lundberg) #4

The former case is what I'm concerned with. I agree that code external to the framework should only see public symbols. However objc code inside the same framework as the swift code in question should ideally be able to see internal swift symbols as well, as they are within the same module.

···

--
Kevin Lundberg

On Jun 4, 2016, at 2:48 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

I ran into a major hurdle this week that basically stopped my work in
its tracks. I've been working on moving a large codebase from an iOS app
target to a framework target, since we have the same code in multiple
app targets and it is problematic to have to remember to add new code to
every single app target when they can all just share a framework.

To be clear: Are you having trouble making the Objective-C and Swift inside your framework talk to each other, or the Objective-C outside your framework talk to the Swift inside your framework?

If it's the latter, then I agree with Jens that this is "works as intended", and you're just going to have to spend some time pasting `public` into your code in a lot of places. But if you're being forced to make Swift APIs public so you can use them from Objective-C *inside* the framework, that might be something worth talking about.

--
Brent Royal-Gordon
Architechies


(Jens Alfke) #5

I agree; I didn’t realize that was your situation. I haven’t tried doing this myself. It sounds like a bug.

—Jens

···

On Jun 4, 2016, at 11:29 AM, Kevin Lundberg via swift-users <swift-users@swift.org> wrote:

However objc code inside the same framework as the swift code in question should ideally be able to see internal swift symbols as well, as they are within the same module.


(Austin Zheng) #6

This is probably a solution to a different issue, but what happens if you declare your "should be visible from Objective-C" swift methods as 'dynamic'?

Austin

···

On Jun 4, 2016, at 11:55 AM, Jens Alfke via swift-users <swift-users@swift.org> wrote:

On Jun 4, 2016, at 11:29 AM, Kevin Lundberg via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

However objc code inside the same framework as the swift code in question should ideally be able to see internal swift symbols as well, as they are within the same module.

I agree; I didn’t realize that was your situation. I haven’t tried doing this myself. It sounds like a bug.

—Jens
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Daniel Dunbar) #7

Unfortunately, this is a limitation of the current model for mixed Obj-C and Swift targets. The Swift code is compiled and optimized as a single module, and the only supported external entry points that result from that are the public API, which is then exposed as the "<module>-Swift.h" header file.

However, this limitation applies to application targets as well, so I'm not sure I understand yet what the blocker is w.r.t. your migration. Can you explain more?

- Daniel

···

On Jun 4, 2016, at 11:29 AM, Kevin Lundberg via swift-users <swift-users@swift.org> wrote:

The former case is what I'm concerned with. I agree that code external to the framework should only see public symbols. However objc code inside the same framework as the swift code in question should ideally be able to see internal swift symbols as well, as they are within the same module.

--
Kevin Lundberg

On Jun 4, 2016, at 2:48 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

I ran into a major hurdle this week that basically stopped my work in
its tracks. I've been working on moving a large codebase from an iOS app
target to a framework target, since we have the same code in multiple
app targets and it is problematic to have to remember to add new code to
every single app target when they can all just share a framework.

To be clear: Are you having trouble making the Objective-C and Swift inside your framework talk to each other, or the Objective-C outside your framework talk to the Swift inside your framework?

If it's the latter, then I agree with Jens that this is "works as intended", and you're just going to have to spend some time pasting `public` into your code in a lot of places. But if you're being forced to make Swift APIs public so you can use them from Objective-C *inside* the framework, that might be something worth talking about.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Kevin Lundberg) #8

Our application target swift code is internal since we don't bother putting public in there as its not shared outside the app(s), obviously. The internally scoped swift code is exported to obj/c through the generated -Swift.h header when in the app target, in addition to anything marked as public (unlike the framework case where only public symbols are found in that header).

This discrepancy seems off, and could possibly trip up anyone else who wants to migrate some mixed language code from an app to a framework. I'd love for there to be some way to import internal symbols in a private/project visible header in objc code in the same framework. I'd be less enthusiastic if consistency went in the other direction where swift code had to be public to be seen in objc code in an app too, but at least that would be consistent behavior for all types of build targets (and if that change resulted in a migrator to do the work that was too much for me to tackle manually, that wouldn't be too bad).

···

--
Kevin Lundberg

On Jun 4, 2016, at 4:00 PM, Daniel Dunbar <daniel_dunbar@apple.com> wrote:

Unfortunately, this is a limitation of the current model for mixed Obj-C and Swift targets. The Swift code is compiled and optimized as a single module, and the only supported external entry points that result from that are the public API, which is then exposed as the "<module>-Swift.h" header file.

However, this limitation applies to application targets as well, so I'm not sure I understand yet what the blocker is w.r.t. your migration. Can you explain more?

- Daniel

On Jun 4, 2016, at 11:29 AM, Kevin Lundberg via swift-users <swift-users@swift.org> wrote:

The former case is what I'm concerned with. I agree that code external to the framework should only see public symbols. However objc code inside the same framework as the swift code in question should ideally be able to see internal swift symbols as well, as they are within the same module.

--
Kevin Lundberg

On Jun 4, 2016, at 2:48 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

I ran into a major hurdle this week that basically stopped my work in
its tracks. I've been working on moving a large codebase from an iOS app
target to a framework target, since we have the same code in multiple
app targets and it is problematic to have to remember to add new code to
every single app target when they can all just share a framework.

To be clear: Are you having trouble making the Objective-C and Swift inside your framework talk to each other, or the Objective-C outside your framework talk to the Swift inside your framework?

If it's the latter, then I agree with Jens that this is "works as intended", and you're just going to have to spend some time pasting `public` into your code in a lot of places. But if you're being forced to make Swift APIs public so you can use them from Objective-C *inside* the framework, that might be something worth talking about.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Kevin Lundberg) #9

Its not just methods, but types themselves. Even if this was a solution
I'd still have to manually fix all build issues by prepending dynamic to
everything that needs it :frowning:

···

On 6/4/2016 2:56 PM, Austin Zheng wrote:

This is probably a solution to a different issue, but what happens if
you declare your "should be visible from Objective-C" swift methods as
'dynamic'?

Austin

On Jun 4, 2016, at 11:55 AM, Jens Alfke via swift-users >> <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

On Jun 4, 2016, at 11:29 AM, Kevin Lundberg via swift-users >>> <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

However objc code inside the same framework as the swift code in
question should ideally be able to see internal swift symbols as
well, as they are within the same module.

I agree; I didn’t realize that was your situation. I haven’t tried
doing this myself. It sounds like a bug.

—Jens
_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users


(Jordan Rose) #10

We don't currently have a way to generate two headers, one to be used internally and one externally. For methods and properties on existing types, it's safe to write a category manually to be included in your .m files, but classes and protocols don't really have a good answer.

This is very similar to another existing problem, that you can't import implementation-detail things into Swift without making them public. However, that problem still needs a lot of design, whereas this one is essentially as simple as "generate two headers". (It's not quite that easy because you need one to import the other rather than having them be independent, but it's still a problem where it's known that the solution will work.)

"Simple" or "easy" does not necessarily mean "quick to implement", though, so we have to balance this against other improvements.

Best,
Jordan

···

On Jun 4, 2016, at 13:00, Daniel Dunbar via swift-users <swift-users@swift.org> wrote:

Unfortunately, this is a limitation of the current model for mixed Obj-C and Swift targets. The Swift code is compiled and optimized as a single module, and the only supported external entry points that result from that are the public API, which is then exposed as the "<module>-Swift.h" header file.

However, this limitation applies to application targets as well, so I'm not sure I understand yet what the blocker is w.r.t. your migration. Can you explain more?

- Daniel

On Jun 4, 2016, at 11:29 AM, Kevin Lundberg via swift-users <swift-users@swift.org> wrote:

The former case is what I'm concerned with. I agree that code external to the framework should only see public symbols. However objc code inside the same framework as the swift code in question should ideally be able to see internal swift symbols as well, as they are within the same module.

--
Kevin Lundberg

On Jun 4, 2016, at 2:48 AM, Brent Royal-Gordon <brent@architechies.com> wrote:

I ran into a major hurdle this week that basically stopped my work in
its tracks. I've been working on moving a large codebase from an iOS app
target to a framework target, since we have the same code in multiple
app targets and it is problematic to have to remember to add new code to
every single app target when they can all just share a framework.

To be clear: Are you having trouble making the Objective-C and Swift inside your framework talk to each other, or the Objective-C outside your framework talk to the Swift inside your framework?

If it's the latter, then I agree with Jens that this is "works as intended", and you're just going to have to spend some time pasting `public` into your code in a lot of places. But if you're being forced to make Swift APIs public so you can use them from Objective-C *inside* the framework, that might be something worth talking about.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Kevin Lundberg) #11

My apologies if I insinuated that the effort would be small or trivial, that wasn’t my intent.

The converse situation that you describe, where objc code must also be public in the framework makes sense. I didn’t mind that myself when I was attempting this since this is an internally used framework only, but ideally that should also be resolved. The fact that swift in frameworks has to be public for this to work today is less a problem for me than the discrepancy between target types.

I logged a radar for the swift -> objc case that I described initially as that’s the use case that affects me most: rdar://26663470.

···

--
Kevin Lundberg
kevin@klundberg.com

On Jun 6, 2016, at 1:34 PM, Jordan Rose <jordan_rose@apple.com> wrote:

We don't currently have a way to generate two headers, one to be used internally and one externally. For methods and properties on existing types, it's safe to write a category manually to be included in your .m files, but classes and protocols don't really have a good answer.

This is very similar to another existing problem, that you can't import implementation-detail things into Swift without making them public. However, that problem still needs a lot of design, whereas this one is essentially as simple as "generate two headers". (It's not quite that easy because you need one to import the other rather than having them be independent, but it's still a problem where it's known that the solution will work.)

"Simple" or "easy" does not necessarily mean "quick to implement", though, so we have to balance this against other improvements.

Best,
Jordan

On Jun 4, 2016, at 13:00, Daniel Dunbar via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

Unfortunately, this is a limitation of the current model for mixed Obj-C and Swift targets. The Swift code is compiled and optimized as a single module, and the only supported external entry points that result from that are the public API, which is then exposed as the "<module>-Swift.h" header file.

However, this limitation applies to application targets as well, so I'm not sure I understand yet what the blocker is w.r.t. your migration. Can you explain more?

- Daniel

On Jun 4, 2016, at 11:29 AM, Kevin Lundberg via swift-users <swift-users@swift.org <mailto:swift-users@swift.org>> wrote:

The former case is what I'm concerned with. I agree that code external to the framework should only see public symbols. However objc code inside the same framework as the swift code in question should ideally be able to see internal swift symbols as well, as they are within the same module.

--
Kevin Lundberg

On Jun 4, 2016, at 2:48 AM, Brent Royal-Gordon <brent@architechies.com <mailto:brent@architechies.com>> wrote:

I ran into a major hurdle this week that basically stopped my work in
its tracks. I've been working on moving a large codebase from an iOS app
target to a framework target, since we have the same code in multiple
app targets and it is problematic to have to remember to add new code to
every single app target when they can all just share a framework.

To be clear: Are you having trouble making the Objective-C and Swift inside your framework talk to each other, or the Objective-C outside your framework talk to the Swift inside your framework?

If it's the latter, then I agree with Jens that this is "works as intended", and you're just going to have to spend some time pasting `public` into your code in a lot of places. But if you're being forced to make Swift APIs public so you can use them from Objective-C *inside* the framework, that might be something worth talking about.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users

_______________________________________________
swift-users mailing list
swift-users@swift.org <mailto:swift-users@swift.org>
https://lists.swift.org/mailman/listinfo/swift-users