Of Public Names, Cocoa and Shared (code) Projects

A Problem

I am building up a library of my own shared code and wish to create EUnit tests for that shared code. However, EUnit takes a dependency on Sugar which results in name collisions with some of the classes in my shared code.

This means I either have to adopt prefixes or some other explicit strategy (such as choosing less intuitive/more awkward names for the classes involved) to avoid those name collisions in my code (since it is the late-comer to the class naming party) or abandon the idea of using EUnit as the platform for my tests.

Neither is particularly appealing. To say the least. :slight_smile:

The Fly in the Ointment

As I understand it, the underlying problem is the lack of proper namespace support in Objective-C. Public classes (etc) are required to have unique names in NOUGAT projects. There is a reference in the documentation site to the use of name mangling to avoid collisions, but this (I presume) proved too complex/fragile since this is not actually the case (for public names).

However, I wonder if this might now also be extended to public names as well, now that we have shared code projects ?

When building an application target any public names in the application are never-the-less private to the application even if they are public within it. i.e. name mangling could surely be used to ensure that application declared class names do not collide with any names in frameworks or libraries referenced by the application. Those referenced libraries and frameworks by definition are not referencing the application defined names so won’t care if they are being mangled. There won’t be any collisions.

It remains the case that when building a framework or library, public names must be unique and mangling cannot be reliably employed to ensure this (or becomes unacceptably complex/fragile) due to the fact that the library or framework will or may be consumed by other projects (be they applications or other libraries/frameworks).

Prior to Shared Code projects then, the primary means of creating re-usable code was via libraries, which precluded the use of public name mangling.

However, with Shared Code projects it is now possible to have code that is shared by potentially many applications which is not part of any library, instead being compiled as part of each application itself.

The Idea…

It occurs to me that in these cases, since my code is being compiled into an application, not a shared library, the compiler should be able to mangle names to avoid those collisions on my behalf, automatically. It should not (cannot) do so building a library target. But this would seem to me to be an acceptable trade-off:

  1. Building a library: No public name mangling. Public names must be explicitly unique (the current situation)

  2. Building an application: Public names are automatically mangled for uniqueness by the compiler, as for non-public names.

If your objective is to create a re-usable library and you know that one of your targets is/will/may be an Objective-C framework then you must take this into account in the public names in your library design (and it perhaps would be advisable to follow the accepted practices in this respect - i.e. explicitly prefixed class names).

But if you are using shared code projects as the vector for sharing code between applications then this need not be an issue or a concern when building those applications.

Need it ? :thinking:

Types don’t need to be public, to be used in shared projects, because all the files in a shared project will built in the context of the “real” project.

Will read the rest and reply in more detail tomorrow.

Hmmm, OK. I think you just solved the problem then. If I understand correctly, I have simply misunderstood the implications of the default assembly visibility of types in a namespace. :blush:

The doc wiki says that this results in types that are:

Only accessible from within this project

For a shared code project I had taken that to mean within that shared code project, as if the shared code project were a library, just one that happens to be compiled into each application.

So if I just remove the public visibility from the types, they will still be accessible from outside of the namespace (i.e. in application code that uses the appropriate namespace or fully qualified names) ?

I can’t believe I didn’t simply try that to see what would happen! :slight_smile:

Yes. There is 1 exception though. Due to JVM limitations itself you can’t access things in different assemblies (ie packages) when they’re not public, on Java.

Thanks Carlo, but if I understood Marc aright, that would only be a problem if the project referencing the shared project was a library, correct ? The limitation then applying to any applications (or other libraries) that then referenced that library.

i.e. Shared Code projects

       --> contains SharedCode namespace with an `assembly` class called Foo

Application A
       --> uses SharedCode  (can reference SharedCode.Foo)

Library A
       --> uses SharedCode (can reference SharedCode.Foo)

Application B
       --> uses Library A (can not reference SharedCode.Foo)

Yes and no. Java is different (And strange). The JVM doesn’t really have the concept of “assemblies” or “projects”. Instead they group by package, which is effectively a namespace. The rules for assembly are enforced by the compiler but in addition, on Java it also enforces the namespace rules (which kinda sucks, but can’t be avoided due to the JVM).

I should probably reword that to “executable”. Shared projects aren’t really projects at all, they don get compiled. All they re are a collection of files that get compiled as part of the project that references them.

As far as the compiler sees, there are only three projects, and all three happen to contain the same “SharedCode” files. For each of the three projects, those file(s) will be compile as part of the respective project, and take part in that project’s visibility resolution and other aspects (for example, if they were swift files, they would use each project’s default namespace, too).

Really all the shared project does is keep you form having to manually maintain the list of shared files in each of the three projects.

Thanks for the additional info guys.

I thought I would be able to deal with this simply by removing the public visibility from the types, but that then fell foul of the “namespace == package” rule on Java, as Carlo warned would happen. :frowning:

It’s late and I’m tired but I can’t help think there must be a way to achieve a consistently useful behaviour in this area somehow, and I’m back to my original notion that it’s Shared Code projects that have brought this to the fore (as Marc says, a Shared Code project is not really a “project” as such at all, but as a way of sharing source in a project-like manner they are perhaps more sensitive to the platform differences in this area ?).

At the moment I am resorting to:

  Foo = {$if COOPER}public{$endif} class

Which feels horrible.

(Though I have to say that the Java rules actually feel more intuitive, to me).

You could just put things in the same namespace, that works everywhere without the public.

But this is shared code. That is, not just common code among a group of closely related targets/projects but intended for broad re-use (using a shared code approach to avoid having to build/deploy a separate library target with any applications that use it).

Lumping everything into the same namespace isn’t an option.

Even if I were to drop that shared code idea and just switch to an actual library target, surely I would then run into the duplicate names of non-mangled public types on NOUGAT ? (when it comes to trying to use EUnit to create tests for that library, which is where I started). :frowning:

But as I said, it’s late and I’m tired and probably missing something. Maybe something will occur to me in the morning. :slight_smile:

And here is the morning and what has occurred to me is that you perhaps meant was to implement the test classes in the same namespace as the classes/types under test ?

I can see this might work for the test runner projects specifically, but I’m not sure it extends to being a generally workable approach. In particular, my shared code involves a “hierarchy” of namespaces, not just one single namespace. For tests it is perhaps workable to place the test for each class in the specific namespace for that class under test, but in more general application code there may be types involved from different namespaces and the application code has to be implemented in just one of those - it can’t be in all of them at once.

Have I missed something ?


Thanks, logged as bugs://75431: Option to allow public Nougat types to be mangled if needed

Awesome! :slight_smile:

The conditional compilation workaround for java public types can keep me going in the meantime with that to look forward to. :slight_smile:

1 Like

bugs://75431 got closed with status fixed.

@ck - I see that this option is in the 2009 beta. Thanks. :slight_smile:

How does one go about making use of/activating the new option ?

The IDE side of this still has to be done. But it’s just a question of placing <MangleTypeNames>True</MangleTypeNames> in the project file.

bugs://75431 got closed with status fixed.

Logged as bugs://i63405.

bugs://i63405 was closed as fixed.