Swift Package must set minimum platform version to same as dependency

When you have a dependency on a package with a range of versions, your package must set the supported platform version to the highest iOS version supported by the dependency. Otherwise it might error with something like The package product 'x' requires minimum platform version 16.0 for the iOS platform, but this target supports 15.0

Does anyone know a way around this? We’d like to have a range of versions of the package that support multiple iOS versions.

For example, GoogleMaps 9.4.0 supports iOS 15 and 10.4.0 supports iOS 16. However, my package, which depends on GoogleMaps, must support iOS 16 no matter what version of GoogleMaps it resolves to because it has the potential to resolve to 10.4.0 (and therefore require iOS 16).

If it resolves to GoogleMaps 9.4.0, it builds fine. If it resolves to 10.4.0, it’ll fail with this error:


error: The package product 'GoogleMaps' requires minimum platform version 16.0 for the iOS platform, but this target supports 15.0 (in target 'MyPackage' from project 'MyPackage')

In our particular use case, we want to create a Swift Package that wraps the GoogleMaps SDK and allows the client to control the version of GoogleMaps they use.

Here's a pared-down example Package.swift:

// swift-tools-version: 6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
  name: "MyPackage",
  platforms: [
    .iOS("15.0")
  ],
  products: [
    .library(
      name: "MyPackage",
      targets: ["MyPackage"]
    )
  ],
  dependencies: [
    .package(
      url: "https://github.com/googlemaps/ios-maps-sdk",
      "9.0.0"..<"11.0.0"
    )
  ],
  targets: [
    .target(
      name: "MyPackage",
      dependencies: [
        .product(
          name: "GoogleMaps",
          package: "ios-maps-sdk"
        )
      ]
    )
  ]
)

Build command:


xcodebuild build -scheme MyPackage -sdk "`xcrun --sdk iphonesimulator --show-sdk-path`" -destination "OS=15.5,name=iPhone 13"

2 Likes

Definitely a hot topic. I can explain how things work now and why. But yes, it can be awkward.

So GoogleMaps has versioned their package correctly. Changing the minimum platform is a major version breaking change. I assume it’s because they’re using some API that is only supported starting with iOS 16 in their newer version. Which means if someone is building it for iOS 15, they would get a compile error.

Essentially for this to work, you would need to have the same semantic versioning for your package and set the appropriate minimum platform version in each of those versions to match GoogleMaps.

2 Likes

I have exactly the same issue.
I’m providing a wrapper around GoogleMaps and as soon as the consumer of my Package uses Google Maps 10.x the SPM resolution fails due to version mismatch.

My Package contains a Core implementation supporting iOS 15 as minimum requirement.
Then the Package defines also additional products, they can be considered extensions.

While the Core product offers core implementations and abstraction, I would like to have “sub” products that have some out of the box implementations for the abstraction.
They would be basically opt-in dependencies.

Apple Maps is an implementation, it keeps the compatibility to iOS 15 minimum
Google Maps V8 is another implementation, it keeps the compatibility to iOS 15 minimum
Google Maps V9 is another implementation, it keeps the compatibility to iOS 15 minimum
Google Maps V10 is another implementation, it requires the compatibility to iOS 16 minimum

So with this showcase seems legit that if the consumer want to use the Google Maps V10 implementation, will require the Application to use iOS 16 as minimum version, which the Core is also compatible with.

Actually the whole Package (standalone) builds correctly.
Core builds since does not break iOS 15 minimum requirement.
GoogleMapsV10Extension also builds since it does not break the iOS 15 minimum requirement

The issue popup when the Package is consumed into an application which consumes GoogleMaps V10 (iOS 16) mismatching with the minimum platform version (iOS 15) defined in for the whole Package.

Anyway, I was able to produce a workaround, but is far from being ideal.

It requires the usage of ProcessInfo().environment["MYPROJECT_IOS_MIN_VERSION"] within the Package.swift definition. That environment variable must be defined when Xcode is opened

From terminal:
MYPROJECT_IOS_MIN_VERSION=15 xed .
or
MYPROJECT_IOS_MIN_VERSION=16 xed .

With that setup, the Package.swift can read and switch the environment value so to properly set the .iOS(.v15) vs .iOS(.v16) platform value.

This is definitely far from ideal. It will increase the complexity to consume the package from the consumer point of view.

I think another potential workaround, “cleaner” which I still didn’t try, is to have another Package.swift into another repository, implementing the extensions.

Also unfortunately it’s still not possible to have two Packages in the same repository as far as I know.

This unfortunately it’s not applicable to my use case since will force me to change the package definitions to public definitions instead which is something I don’t want to expose to.

It might be a try out someone else can give a try to.

Simplest solution is likely to unify all of your targets with an iOS 15 deployment target, and for those that actually require iOS 16, use @available markup to limit the APIs. That way you don't have to worry about mismatched deployment targets, though it does have the downside of having to maintain the markup.

Frankly, this is really something SPM should synthesize for you. If my target says iOS 16 and the user has an iOS 15 target, all of the APIs should be marked with @available rather than breaking the build.

2 Likes

Thanks @Jon_Shier for the reply. For my use case, and @vash use case, we depend on GoogleMaps, 3rd party, which made their SPM iOS 16.

So we are out of control and applying your suggestion is not possible unfortunately.