They write, without referencing a source: "Although Apple discourage creating umbrella framework". Is that true, do Apple discourage umbrella frameworks, if so why and is it a very strong discourage or a mild one?
Here's a citation for that, from Technote 2435:
An umbrella framework is a framework bundle that contains other frameworks. While it is possible to create umbrella frameworks for macOS apps, doing so is unnecessary for most developers and is not recommended. Umbrella frameworks are not supported on iOS, watchOS, or tvOS.
While that technote is older, that advice is still relevant. It's quite normal today for an app to have a large dependency tree, and that's a scenario where it's easy to get into trouble. Consider an example app that looks like this:
App
├── Library1
│ └── LibraryA-1.0.0
└── Library2
└── LibraryA-1.0.3
Let's further assume that LibraryA in that diagram is written in Objective-C, or Swift. What happens when the app launches is that the system needs to load and map all of the symbols each binary contains. Since Library 1 and Library 2 both depend on Library A, but there are two different implementations, the same symbols are going to get loaded, twice. This creates a runtime conflict of undefined behavior of which implementation gets used — if Library 1 calls LibraryA.method1()
, it might actually call into the method implementation from LibraryA-1.0.0
... or it might wind up calling into the implementation from LibraryA-1.0.3
, of Library 2, because which one gets called is the undefined behavior. So now you have the possibility of Library1 calling into a library version that it didn't test against and is controlled by some other library that may not even be maintained by the same organization. Pushing this even further, let's say that Library 1 calling into Library 2's copy of Library A crashes. The author of Library 1 has no ability to reproduce or debug, because the crash is entirely dependent on a copy of LibraryA loaded into the same process that they have no control over. Yikes!
One way the above could be dealt with is if both Library1 and Library 2 declare a dependency on Library A through a Swift Package, using a semantic versioning scheme, such as 1.0
to accept any bug fix version of 1.0. At build time, the system can see that both libraries depend on Library A, and resolve that the most recent version, such as 1.0.3
, so that everyone gets the same implementation, and that Libraries 1 and 2 do not need to bundle their own version specific copies, and Library A can be a sibling. Then the app structure looks like this, with Libraries 1 and 2 both pointing at their LibraryA sibling:
App
├── Library1
├── Library2
└── LibraryA
I've talked to a lot of developers about this subject over many years, and the specifics of any situation matter a lot. This is just one example, with one solution, and there are many other situations that would need a different solution, but I hope it highlights some of the issues that a dependency tree cast as an umbrella framework can create.
— Ed Ford, DTS Engineer