static library - Xcode 9 - Swift

I have to deliver SDK to my customers. SDK is about accepting different Payment methods.


This is high level strucure of the SDK.


AllPayments.Framework - Dynamic framework (Swift) - It has all Public APIs for customers to consume.

Verification.Framework - Dynamic framework (Swift) - AllPayments.Framework uses this for payment verification purpose.

Network.Framework - Dynamic framework (Swift) - AllPayments.Framework, Verification.framework uses this for network operations


(for now i have taken 3 components but list is long).


I have to explicity mention in my release/integration guide that customer has to first integration AllPayments.frameworks and explicitly needs to add Verification and Network frameworks because AllPayments is depended on it.


In order to make it more simple, how best i can utilize the Swift Static framework of XCode 9?

Should i create AllPayments as static library so that it swallow the Verification.Framework & Network.Framework and customer has to use only AllPayments static library. But challenge here it "I can not distribute swift static library" https://forums.developer.apple.com/thread/83706


Please suggest alternate approch.

Accepted Reply

A static library won't really help. Distributing a single static library or a single framework is still the same problem.


(In general, you would want to distribute a single component, and in general you would want to distribute it as a framework. That means you need to build the framework as a single target in your Xcode the project. You don't need static libraries for that, since you can compile all of the source files from all of the current 3 or more frameworks into one framework target.)


However, there is a larger problem. Currently, any framework or static library that contains Swift code is tied to the version of the Swift compiler that produced it, and each different version of Xcode has a different version of the Swift compiler. That includes the Xcode 9 betas — since there have been 6 so far, there are 6 Swift compiler versions.


Any framework or library you distribute will only work for customers using the exact same Swift compiler, which basically means the exact same Xcode version. (You could distribute multiple components, one for each supported version, but you would have to have all of the Xcode versions installed to produce them.)

Replies

A static library won't really help. Distributing a single static library or a single framework is still the same problem.


(In general, you would want to distribute a single component, and in general you would want to distribute it as a framework. That means you need to build the framework as a single target in your Xcode the project. You don't need static libraries for that, since you can compile all of the source files from all of the current 3 or more frameworks into one framework target.)


However, there is a larger problem. Currently, any framework or static library that contains Swift code is tied to the version of the Swift compiler that produced it, and each different version of Xcode has a different version of the Swift compiler. That includes the Xcode 9 betas — since there have been 6 so far, there are 6 Swift compiler versions.


Any framework or library you distribute will only work for customers using the exact same Swift compiler, which basically means the exact same Xcode version. (You could distribute multiple components, one for each supported version, but you would have to have all of the Xcode versions installed to produce them.)

Please suggest alternate approch.

What QuinceyMorris said plus…

One option here is to distribute the bulk of your code as Swift source code, so that developers can build that code for themselves, and then have a small, critical component written in Objective-C that you distribute as a binary (a static library would probably be best). This allows you to do the bulk of your work in Swift while maintaining some degree of control.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

"One option here is to distribute the bulk of your code as Swift source code, so that developers can build that code for themselves"


I am not sure how any business can share their source code with end-users and survive. We are in the same situation and need to deliver a swift umbrella framework which contains another 12 swift dynamic frameworks. We don't want to share the source code with end-users of our framework. Is there any possible way to do this without that? We would like to share a single framework with users. Please let us know.

In some cases, most of the source code doesn't contain any trade secrets. A good example is the clang (C/Obj-C/C++) compiler, which is open source. However the version of the clang compiler version that ships with Xcode is an Apple-specific variant that links with private code.


If that approach doesn't work for you, the best alternative is to ship a framework for a specific Xcode version (or, usually, a couple of Xcode versions), and require users to be on a version you support.


This situation is "temporary" (only 4 years so far) because it will be resolved when the Swift ABI is finalized. This should happen in 2018 or 2019 if all goes well.

If that approach doesn't work for you, the best alternative is to ship a framework for a specific Xcode version … and require users to be on a version you support.

Be careful here. Being on the same version of Xcode is necessary but may not be sufficient. Xcode’s framework build products (for Swift) were not designed to be shared like this, and it’s quite possible that you’ll run into issues other than the Xcode version.

This situation is "temporary" (only 4 years so far) because it will be resolved when the Swift ABI is finalized.

Again, you have to be careful here. ABI stability is only one step on the road to being able to ship binary frameworks; you also need module format stability. The Swift ABI Stability Manifesto is a good resource here.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

>> Xcode’s framework build products (for Swift) were not designed to be shared like this


Can you be any more specific about this? There's no apparent difference conceptually between sharing like this and using private embedded Swift frameworks within a multi-target app.


Also, if you construct a framework that is an Obj-C API wrapper around mostly (or even just some) Swift code, is such a framework distributable in built form? I had the impression that it was, but now I'm not sure.

>>> If that approach doesn't work for you, the best alternative is to ship a framework for a specific Xcode version (or, usually, a couple of Xcode versions), and require users to be on a version you support.

I was trying to do this(I am not worried about this version issue for now). But the problem is that, I would like to embed the other 12 swift dynamic frameworks to my umbrella framework. But when I distribute the umbrella framework to end-user and when they add it to an iOS app, it asks for the 12 sub-frameworks which is not a desired behavior. So I was wondering about the Apple recommended approach to such an issue where you want to embed swift dynamic frameworks to a larger umbrella framework and deliver it to end-users. Please let us know.

That's a different issue. Apple recommends against 3rd-party developers trying to use umbrella frameworks. In that case (Swift binary issues aside), you would do better to create the 12 libraries as static libraries, and use an Xcode target to link them into a single dynamic framework.

>>> you would do better to create the 12 libraries as static libraries, and use an Xcode target to link them into a single dynamic framework.


Thanks for the quick reply. I was trying the exact same thing with a sample swift "FrameworkA" built as Static Framework(Changed Mach-O Type to static library) trying to link in a dynamic framework "FrameworkB". Then tried to integrate with a Test app. It works on my macbook pro, but the moment I send that app to another machine, it complains "Missing required module 'FrameworkA'" in that which means the umbrella framework is not exactly embedding the FrameworkA but just links it.


Only option I have found till now, is to deliver both Static Framework and Aggregate dynamic framework to end-user which is inconvenient for the users and defeats the purpose of having an umbrella dynamic framework. That was the reason for my question, as what the Apple recommended approach in this case. Hope my question is clear.

"Missing required module 'FrameworkA'" sounds like a tooling problem. If you had previously linked an against a module FrameworkA, and the module definition still exists, then the linker might be looking for a framework FrameworkA to link against, but that bundle no longer exists (since you changed it to a static library).


There is a module cache in ~/Library/Developer/Xcode/DerivedData/ModuleCache. AFAIK this is just a cache, so you can just delete this and contents will be re-created as necessary. If your "old" module FrameworkA is still in there, deleting it may solve the linker problem.


OTOH, I'm only stating what I've observed, and have no sure knowledge of what it's safe to delete in there. My experience has generally been that static libraries are a huge PITA in Xcode, and that it's easier just to recompile the source files directly if you're not using them via frameworks. At least, it's easier to do something obvious but slow like that, than to have to actually understand how static libraries work in Xcode.

Can you be any more specific about this? There's no apparent difference conceptually between sharing like this and using private embedded Swift frameworks within a multi-target app.

There is a difference, namely the environment in which you built the framework. For example, back in the day Swift build products contained full paths, so you’d run into problems if you moved those build products from one place to another even on the same machine.

I haven’t heard folks complain about that recently, so it’s possible that it’s been fixed. However, the take-home message is the same: unsupported activities are unsupported. The fact that something appears to work doesn’t mean that it actually does work in all situations, or that it will continue to work in the future. You already understand this when it comes to APIs, but the same rules also apply to tools.

Also, if you construct a framework that is an Obj-C API wrapper around mostly (or even just some) Swift code, is such a framework distributable in built form?

Not in a supported fashion. Imagine two folks doing this independently, using different versions of Xcode. The Swift code in framework A is expecting Swift runtime N, while the Swift code in framework B is expecting Swift runtime M. This probably won’t link but, if it does, it is likely to end badly when both A and B are loaded into the same process.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks again for the quick response. I have cleared "Derived Data" folder(Cleared from Library/Developer path as mentioned and from local project/Derived Data folder) and tried too before copying the project to another Macbook pro and testing. As I mentioned, it works fine on my machine, but not on any other machines. I have uploaded the sample project to the following gitlab page https://gitlab.com/akhilcb/AppA . You can clone it and see that it shows this error even though "FrameworkB" with static framework "FrameworkA" embdedded is showing "Missing required module 'FrameworkA'" error when you run with target as iOS simulator.

… back in the day Swift build products contained full paths, so you’d run into problems if you moved those build products from one place to another even on the same machine.

Apparently that’s still the case.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"