4

I'm working on a iOS project that need to support iOS 10+. I would like to build some new features with SwiftUI and make them only available for our iOS 13+ users by using @available(iOS 13.0, *). This works fine as long as I use only out of the box components.

However, when importing a community-made SwiftUI component that is targeted iOS 13 and up I get the following error:

Error as shown in XCode

Compiling for iOS 10.0, but module 'SwiftUIPager' has a minimum deployment target of iOS 13.0: 
DerivedData/MyApp-qbarasdasdaasdbsp/Build/Products/Debug-iphonesimulator/SwiftUIPager.swiftmodule/x86_64-apple-ios-simulator.swiftmodule

This makes sense because the module has set iOS 13+ as the SupportedPlatform. I've suggested the author to lower the target to iOS 10 and add @available(iOS 13.0, *)'s everywhere but of course that is also not ideal because it renders the SupportedPlatform attribute of SPM useless.

Is there a way of including packages like this in a project that wants to support lower a lower target than the package supports? Is this a flaw on the design of the Swift package manifest file?

Tieme
  • 62,602
  • 20
  • 102
  • 156

2 Answers2

1

Here is a working solution. Tested with Xcode 11.4 / iOS 13.4

1) Create iOS framework target (let's name it ModernProxy for reference) with deployment target > 13.0 and make it dependent on modern SwiftUI based package (ModernLibrary in this demo), as on example below

demo1

2) Make your legacy target embed modern framework, as on example below

demo2

3) In ModelProxy framework create controller class with default non-argument init that wraps SwiftUI root UIHostingController with view from external package, as on example below

import SwiftUI
import ModernLibrary // SwiftUI based external package

public class ModernProxyController: UIHostingController<ModernView> {
    public init() {
        super.init(rootView: ModernView())
    }

    @objc required dynamic init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

and make this controller as framework's principal class (actually in more complex case the principal class can play some factory role to return many controllers by protocol, but here is one for simplicity)

demo3

4) In your main target ViewController (that one where you want to inject SwiftUI modern view) load dynamically proxy framework bundle and instantiate controller via principal class, as on below example

class ViewController : UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // ... any other code here

        if #available(iOS 13.0, *) {
            if let proxyClass = Bundle(identifier: "com.Testing.ModernProxy")?.principalClass as? UIViewController.Type {
                let controller = proxyClass.init()
                self.addChild(controller)
                controller.view.translatesAutoresizingMaskIntoConstraints = false
                view.addSubview(controller.view)
                // ... add any needed constraints
            }
        } else {
            // Fallback on earlier versions
        }
    }

    // ... other controller code here
Asperi
  • 228,894
  • 20
  • 464
  • 690
0

Currently SPM is not compatible with these scenarios.

My current workaround is to manually add package files to my project and add @available(iOS 13.0, *) checks where needed.

To make it less dirty I:

  • Import packages as git submodules
  • Create forks to save my compatibility changes and create Pul Requests to the original projects
  • Add @available(iOS 13.0, *) to all classes, structs, and anywhere where a compatibility warning is shown by Xcode.

I hope that Apple will at some point add this functionality to SPM.

Rivera
  • 10,792
  • 3
  • 58
  • 102
  • how do you do this more specifically? I’m trying to use swiftuicharts framework (made for iOS 13) on an app that has a deployment target of iOS 10. https://stackoverflow.com/questions/67309430/compiling-for-ios-10-3-but-module-swiftuicharts-has-a-minimum-deployment-targ – Abv Apr 29 '21 at 18:56
  • @Abv I updated the answer with more detail. I use git submodules to better keep track of changes as you will need to modify the original source code of swiftuicharts in your case. – Rivera Nov 18 '21 at 13:51