I am developing a framework for Swift that is independent of the OS flavor it should work on iOS, macOS etc. The framework only contains an API for calculations, and therefore only imports Foundation. It doesn't deal with views or anything and UIKit or AppKit are not used.
When I started the project a while ago, I was following some tutorial and ended up with two targets: MyFramework_macOS and MyFramework_iOS. But in hindsight, I'd rather have only one target: MyFramework.
Is this possible, or do I need to maintain targets for each OS?
Yes and no. Binaries are entirely different. MacOS needs the X86_64 architecture, iOS, various flavors of arm64. You can make a fat binary using lipo during the final build steps, or you can build a .xcframework that was discussed at WWDC. Those might be options for a multi-binary framework.
Do you actually need a framework? Unless your source is top secret, what you are doing sounds like a good fit for a package instead, which would not need to care about architectures or bundling details.
Yes, I have looked into that. But alas, my Mac is too old for the newest Xcode version, which as I understand is required to work with SPM. I am on High Sierra and Xcode 10 dot something (not at my Mac right now).
You can still use packages, you just need to do it indirectly by running swift package generate-xcodeproj, and then dragging that project into the same workspace as whatever client project. Such a generated project supports all Apple targets automatically.
You can make a fat binary using lipo during the final build steps
To be clear, this is commonly done but it’s not officially supported. All the architectures in a universal binary must be for the same platform. If you mix platforms you will eventually bump into some corner case that fails.
If you want to target different platforms, use an XCFramework.
So I was able to create a Package using the swift init package and swift package generate-xcodeproj commands, added the sources from the old framework, and made it compile and test. The product is still called MyFramework.framework, which I then added to the "Link Binary with Libraries" section under "Build Phases" in the client project. And again, it compiles.
When I tried using 1 target for all platforms like half a year ago, my example target containing a watchOS and an iOS app, both importing the framework, couldn't do clean build, I had to build the framework for watch first, then hit build for the example target.
There is a setting, I think it's called Product Name, which defines what the person has to write after the import keyword, you should set it to the same value for all your different platform targets.