Platform per target?

Hi,

I am developing a package which contains several targets (products). One of them is supposed to support from iOS 10, and another one is a SwiftUI wrapper, which supports from iOS 13.

I want something like this (KingfisherSwiftUI depends on Kingfisher) :

products: [
    .library(name: "Kingfisher", targets: ["Kingfisher"], platforms: [.iOS(.v10)]),
    .library(name: "KingfisherSwiftUI", targets: ["KingfisherSwiftUI"], platforms: [.iOS(.v13)])
]

But now as I know, we can only specify the platform for the whole package.

Is there a way to specify a different platform per target?

Or is there an alternative way for it?

5 Likes

Now I have a Package.swift in which the platforms is set to `.iOS(.v10):

When I add KingfisherSwiftUI to my app (the deploy target of app is iOS 13), I can build and run the app without a problem. However, when I try to archive it, I got:

"No such module 'Combine'"

It seems SPM (or Xcode) is trying to link it against an environment without Combine. I wonder is there a workaround?

Related issue: No such module 'Combine' · Issue #1295 · onevcat/Kingfisher · GitHub

Your request is a good one.

For now, I only know of two workarounds, neither of which is all that great:

  • Make KingfisherSwiftUI “compatible” with the other platforms/versions using #if, #available and the like. Then document well to clients that while it is designed to universally compile, it should not actually be expected to do anything meaningful on all platforms.
  • Describe to clients an environment variable they can set to choose one variant or the other (at .v10 without the SwiftUI, or with both products at .v13). Then query the environment in Package.swift and adjust the Package instance accordingly. The downside is that clients who use the non‐default won’t be able to just double‐click the package to open it in Xcode. In order to provide an environment they will have to use swift build, generate-xcodeproj or xcodebuild. open .../Package.swift might also inherit the environment if Xcode is not yet running, but I haven’t tried it.

P.S. The compile time #if statement you might be looking for is #if canImport(Combine), the run time check is if #available(iOS 13, ...), and the run time symbol availability declaration is @available(iOS 13, ...).

Thanks for the reply!

Make KingfisherSwiftUI “compatible” with the other platforms/versions using #if ...

I wonder whether this can work or not. It seems the Combine cannot be found, so adding a #if canImport(Combine) might lead to a removal of everything in the KingfisherSwiftUI target when archiving. But I would have a try.

Maybe at last, I would have to separate the KingfisherSwiftUI into another repo. It would be a pity.

Yes. #if is a compile time check, #available is a run time check. You will need to apply one or the other or both in various places. It depends how much variety your package needs to support. Being as broad as possible would require using Combine something like this:

if #available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, *) {
    // Rules out older Apple platforms.

    #if canImport(Combine)
    // Rules out Linux, Windows, Android, etc.

    // ...

    #endif
}
1 Like

I tried going down a separate repo route (since I have too many files that are UIKit-only) and hit a dead end. Seems like Swift Packages are designed to be cross-platform and nothing else. I tried adding iOS as the only platform in the manifest, but it still complained that UIKit is not available when building for macOS.

let package = Package(
    name: "MyLibrary",
    platforms: [.iOS(.v11)],
    ...
)

There seems to be a minimum platform version for all of them, so specifying a platform here just changes the minimum supported version of the platform but the other platforms remain supported with the default minimal version.

1 Like

Is there a way to exclude platforms from a Swift Package? If I can accomplish this, then I can put the package in the parent's repo and consume the parent as a local dependency. No extra repo and I could modify the source code in one place in Xcode as well.

Btw I really like @onevcat's suggestion about adding an optional platform attribute on the product library in the manifest.

canImport is good, but not enough I believe. Even though you will have compiler/run time checks, SPM seems to be linking strongly Combine/SwiftUI in release builds.

We have a similar issue in Moya, here's a thread I created for our Combine support and troubleshooting. Was trying to figure out a possible weak-linking scenario in SPM, but doesn't seem like there is a way. Not sure where to go next.

I believe this is also a bit of a separate issue though in that the command line commands for SPM all invoke with a target of MacOS. See Use SPM to build iOS target - #7 by Keith

Then neither am I, unfortunately.

I would like to clarify though that per‐target platforms would be a rather straightforward evolution proposal that would be unlikely to see any resistance. I have never said otherwise; most of my comments have been about how to work around it in the meantime.

1 Like

ah yeah same, would love to see it go through evolution process